| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832 |
- <template>
- <div class="collapsible-toolbar" :class="{ collapsed: isCollapsed }">
- <div class="toolbar-content" v-show="!isCollapsed">
- <div class="sidebar-content">
- <div class="sidebar-item feature-sidebar-item" v-if="!onlyEnglish"
- :class="{ active: activeSubmenu === 'cocoai' }"
- @click="toggleSubmenu('cocoai')">
- <svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M12 2L2 7l10 5 10-5-10-5z"></path>
- <path d="M2 17l10 5 10-5"></path>
- <path d="M2 12l10 5 10-5"></path>
- </svg>
- <span class="item-label">Coco AI</span>
- </div>
- <div class="sidebar-item feature-sidebar-item" v-if="!onlyEnglish"
- :class="{ active: activeSubmenu === 'uploadFile' }"
- @click="toggleSubmenu('uploadFile')">
- <svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M12 16V4"></path>
- <path d="M7 9l5-5 5 5"></path>
- <path d="M4 16.5v1.5A2 2 0 006 20h12a2 2 0 002-2v-1.5"></path>
- <path d="M5 14h14"></path>
- </svg>
- <span class="item-label">{{ lang.ssUploadFile }}</span>
- </div>
- <div class="sidebar-divider" v-if="!onlyEnglish"></div>
- <!-- <div class="sidebar-item" :class="{ active: activeSubmenu === 'page' }" @click="toggleSubmenu('page')">
- <svg class="item-icon" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M12.8332 1.83398H5.49984C5.01361 1.83398 4.54729 2.02714 4.20347 2.37096C3.85966 2.71477 3.6665 3.18109 3.6665 3.66732V18.334C3.6665 18.8202 3.85966 19.2865 4.20347 19.6303C4.54729 19.9742 5.01361 20.1673 5.49984 20.1673H16.4998C16.9861 20.1673 17.4524 19.9742 17.7962 19.6303C18.14 19.2865 18.3332 18.8202 18.3332 18.334V7.33398L12.8332 1.83398Z"
- stroke="currentColor" stroke-width="1.83333" />
- <path id="Vector_2" d="M12.8335 1.83398V7.33398H18.3335" stroke="currentColor" stroke-width="1.83333" />
- </g>
- </svg>
- <span class="item-label">{{ lang.ssPage }}</span>
- </div> -->
- <div class="sidebar-item" v-if="!onlyEnglish" :class="{ active: activeSubmenu === 'interactive' }"
- @click="toggleSubmenu('interactive')">
- <svg class="item-icon" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Frame">
- <path id="Vector"
- d="M4.44727 14.6738V14.5449L5.84805 13.041H6.02422L9.43164 16.2766V7.46797L9.4875 7.41211H11.9969L12.0098 7.425V13.5137H16.7234L16.8781 13.6684V18.9707H18.5969V12.7789L17.6086 11.7949H13.5996V6.62578L12.7531 5.7793H8.73125L7.8418 6.66875V12.6844L6.60859 11.4512H5.23789L2.85742 13.9863V15.2324L6.35508 18.9707H8.60234L4.44727 14.6738Z"
- fill="currentColor" />
- <path id="Vector_2"
- d="M2.49219 2.79297V10.5273H6.40234V8.76562H4.25391V4.55469H17.7461V8.76562H15.0391V10.5273H19.4219V2.79297H2.49219Z"
- fill="currentColor" />
- </g>
- </svg>
- <span class="item-label">{{ lang.ssInteract }}</span>
- </div>
- <div class="sidebar-item" v-if="!onlyEnglish" :class="{ active: activeSubmenu === 'aiapp' }"
- @click="toggleSubmenu('aiapp')">
- <svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <rect x="3" y="3" width="7" height="7" />
- <rect x="14" y="3" width="7" height="7" />
- <rect x="14" y="14" width="7" height="7" />
- <rect x="3" y="14" width="7" height="7" />
- </svg>
- <span class="item-label">{{ lang.ssAiApp }}</span>
- </div>
- <div class="sidebar-item" v-if="!onlyEnglish" :class="{ active: activeSubmenu === 'h5page' }"
- @click="toggleSubmenu('h5page')">
- <svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <circle cx="12" cy="12" r="10" />
- <path d="M2 12h20" />
- <path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z" />
- </svg>
- <span class="item-label">{{ lang.ssInteractiveWebpage }}</span>
- </div>
- <!-- <div class="sidebar-item" @click="handleToolClick('video')">
- <svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <rect x="3" y="4" width="18" height="16" rx="2" ry="2" />
- <polygon points="10 9 16 12 10 15 10 9" />
- </svg>
- <span class="item-label">{{ lang.ssVideo }}</span>
- </div> -->
- <!-- <div class="sidebar-item" @click="handleToolClick('creative')">
- <svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <circle cx="12" cy="12" r="10" />
- <line x1="12" y1="8" x2="12" y2="16" />
- <line x1="8" y1="12" x2="16" y2="12" />
- </svg>
- <span class="item-label">{{ lang.ssCreative }}</span>
- </div> -->
- <div class="sidebar-item" v-if="!onlyEnglish" :class="{ active: activeSubmenu === 'multimedia' }"
- @click="toggleSubmenu('multimedia')">
- <svg class="item-icon" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M20.1668 17.4167C20.1668 17.9029 19.9737 18.3692 19.6299 18.713C19.286 19.0568 18.8197 19.25 18.3335 19.25H3.66683C3.1806 19.25 2.71428 19.0568 2.37047 18.713C2.02665 18.3692 1.8335 17.9029 1.8335 17.4167V4.58333C1.8335 4.0971 2.02665 3.63079 2.37047 3.28697C2.71428 2.94315 3.1806 2.75 3.66683 2.75H8.25016L10.0835 5.5H18.3335C18.8197 5.5 19.286 5.69315 19.6299 6.03697C19.9737 6.38079 20.1668 6.8471 20.1668 7.33333V17.4167Z"
- stroke="currentColor" stroke-width="1.83333" />
- </g>
- </svg>
- <span class="item-label">{{ lang.ssMultimedia }}</span>
- </div>
- <div class="sidebar-item" :class="{ active: activeSubmenu === 'english' }" @click="toggleSubmenu('english')" v-if="userJson.org == '00bc1e79-b627-11f1-b66d-005056924926' || userJson == '45facc0a-1211-11ec-80ad-005056b86db5'">
- <svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z"></path>
- <path d="M19 10v2a7 7 0 01-14 0v-2"></path>
- <line x1="12" y1="19" x2="12" y2="23"></line>
- <line x1="8" y1="23" x2="16" y2="23"></line>
- </svg>
- <span class="item-label">{{ lang.ssEnglish }}</span>
- </div>
- <div class="sidebar-item" v-if="!onlyEnglish && (userJson.organizeid == '3d2d45b1-703b-11f1-9985-005056924926' || userJson == '45facc0a-1211-11ec-80ad-005056b86db5' || props.userid == '5943e08c-b7d4-11ed-8d51-005056b86db5')" :class="{ active: activeSubmenu === 'cocoLearn' }"
- @click="toggleSubmenu('cocoLearn')">
- <svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"></circle><path d="M12 1v6m0 6v6"></path><path d="M1 12h6m6 0h6"></path></svg>
- <span class="item-label">{{ lang.ssCrossSubject }}</span>
- </div>
- </div>
- </div>
- <div class="submenu" :class="{ visible: activeSubmenu === 'cocoai' }">
- <div class="submenu-title" style="margin-bottom: 0;">
- <div class="title">Coco AI</div>
- <div class="close-icon" @click="toggleSubmenu('cocoai')">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 3">
- <g id="Component 1">
- <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
- </g>
- </g>
- </svg>
- </div>
- </div>
- <div class="submenu-content">
- <AiChat ref="aiChatRef" :userid="props.userid" :userJson="userJson" :courseTitle="props.courseTitle" />
- </div>
- </div>
- <div class="submenu" :class="{ visible: activeSubmenu === 'uploadFile' }">
- <div class="submenu-title" style="margin-bottom: 0;">
- <div class="title">{{ lang.ssUploadFile }}</div>
- <div class="close-icon" @click="toggleSubmenu('uploadFile')">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 3">
- <g id="Component 1">
- <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
- </g>
- </g>
- </svg>
- </div>
- </div>
- <div class="submenu-content">
- <template v-if="readingFile">
- <div class="reading-file">
- <div class="loading-spinner"></div>
- <div class="reading-text">{{ lang.ssReadingFile }}</div>
- </div>
- </template>
- <template v-else-if="!showFileConfirmModal && !exportingDialog">
- <FileInput accept=".pptx,.ppt,.pdf" @change="handleFileUpload">
- <div class="upload-dropzone">
- <div class="upload-dropzone-icon">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
- <path d="M12 15V4"></path>
- <path d="M7 9l5-5 5 5"></path>
- <path d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2"></path>
- </svg>
- </div>
- <div class="upload-dropzone-title">{{ lang.ssDragAndDrop }}</div>
- <div class="upload-dropzone-subtitle">{{ lang.ssSupportPptx }}</div>
- <!-- <div class="upload-dropzone-footnote">同类型文件支持批量导入,跨类型文件请分开处理</div> -->
- </div>
- </FileInput>
- </template>
- <template v-else-if="!exportingDialog">
- <div class="file-confirm-inline">
- <div class="file-info">
- <div class="file-title">{{ lang.ssFileDetected }}{{ currentFileName }}</div>
- <div class="file-subtitle">{{ currentFileName }}({{ lang.ssTotalPages.replace('{count}',
- pageCount.toString()) }})</div>
- </div>
- <div class="import-options">
- <div class="import-option" :class="{ active: selectedImportOption === 'page' }"
- @click="selectedImportOption = 'page'">
- <div class="option-icon">
- </div>
- <div class="option-text">
- <div class="option-title">{{ lang.ssImportAsSlide }}</div>
- <div class="option-desc">{{ lang.ssImportAsSlideDesc }}</div>
- </div>
- </div>
- <div v-show="false" class="import-option" :class="{ active: selectedImportOption === 'library' }"
- @click="selectedImportOption = 'library'">
- <div class="option-icon">
- </div>
- <div class="option-text">
- <div class="option-title">{{ lang.ssImportAndSave }}</div>
- <div class="option-desc">{{ lang.ssImportAndSaveDesc }}</div>
- </div>
- </div>
- </div>
- <div class="modal-buttons">
- <button class="cancel-btn" @click="cancelFileUpload">{{ lang.ssCancel }}</button>
- <button class="confirm-btn" @click="confirmFileUpload">{{ lang.ssConfirm }}</button>
- </div>
- </div>
- </template>
- <template v-if="exportingDialog">
- <div class="progress-inline">
- <div class="progress-header">
- <!-- <span class="file-name">{{ currentFileName }}</span>
- <span class="progress-percent">{{ importProgress }}%</span> -->
- <div class="upload-task-main">
- <div class="upload-task-name">{{ currentFileName }}</div>
- <div class="upload-task-meta">{{ lang.ssCoursewareFile }} · {{ formatFileSize(currentFileSize) }}</div>
- </div>
- <div class="upload-task-side">
- <div class="upload-task-percent">{{ importProgress }}%</div>
- <button type="button" class="upload-task-action" :class="{ 'upload-task-close': !exporting }"
- @click="handleParsingClose">{{ exporting ? lang.ssCancel : '×' }}</button>
- </div>
- </div>
- <div class="progress-bar">
- <div class="progress-fill" :style="{ width: importProgress + '%' }"></div>
- </div>
- <div class="progress-loading" v-if="exporting">{{ lang.ssUploading }}</div>
- <!-- <button class="close-btn" @click="handleParsingClose">{{ lang.ssClose }}</button> -->
- </div>
- </template>
- </div>
- </div>
- <div class="submenu" :class="{ visible: activeSubmenu === 'page' }">
- <div class="submenu-title">
- <div class="title">{{ lang.ssAddTemplatePage }}</div>
- <div class="close-icon" @click="toggleSubmenu('page')">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 3">
- <g id="Component 1">
- <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
- </g>
- </g>
- </svg>
- </div>
- </div>
- <div class="submenu-item-box2">
- <div class="submenu-item" @click="handleToolClick('titlepage')">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector" d="M8 14V8H40V14M18 40H30M24 8V40" stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssTitlePage }}</span>
- </div>
- <div class="submenu-item" @click="handleToolClick('ImagePage')">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M38 6H10C7.79086 6 6 7.79086 6 10V38C6 40.2091 7.79086 42 10 42H38C40.2091 42 42 40.2091 42 38V10C42 7.79086 40.2091 6 38 6Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_2"
- d="M17 20C18.6569 20 20 18.6569 20 17C20 15.3431 18.6569 14 17 14C15.3431 14 14 15.3431 14 17C14 18.6569 15.3431 20 17 20Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_3" d="M42 30L32 20L10 42" stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssImagePage }}</span>
- </div>
- <div class="submenu-item" @click="handleToolClick('ContentPage')">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M28 4H12C10.9391 4 9.92172 4.42143 9.17157 5.17157C8.42143 5.92172 8 6.93913 8 8V40C8 41.0609 8.42143 42.0783 9.17157 42.8284C9.92172 43.5786 10.9391 44 12 44H36C37.0609 44 38.0783 43.5786 38.8284 42.8284C39.5786 42.0783 40 41.0609 40 40V16L28 4Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_3" d="M32 26H16" stroke="currentColor" stroke-width="4" />
- <path id="Vector_4" d="M32 34H16" stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssContentPage }}</span>
- </div>
- <div class="submenu-item" @click="handleToolClick('ImageTextPage')">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M18 6H8C6.89543 6 6 6.89543 6 8V40C6 41.1046 6.89543 42 8 42H18C19.1046 42 20 41.1046 20 40V8C20 6.89543 19.1046 6 18 6Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_2"
- d="M40 6H30C28.8954 6 28 6.89543 28 8V40C28 41.1046 28.8954 42 30 42H40C41.1046 42 42 41.1046 42 40V8C42 6.89543 41.1046 6 40 6Z"
- stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssImageTextPage }}</span>
- </div>
- </div>
- <FileInput accept="application/vnd.openxmlformats-officedocument.presentationml.presentation"
- @change="handleFileUpload" v-if="false">
- <div class="submenu-upload">
- <div class="submenu-icon">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M24.5 17.5V22.1667C24.5 22.7855 24.2542 23.379 23.8166 23.8166C23.379 24.2542 22.7855 24.5 22.1667 24.5H5.83333C5.21449 24.5 4.621 24.2542 4.18342 23.8166C3.74583 23.379 3.5 22.7855 3.5 22.1667V17.5"
- stroke="currentColor" stroke-width="2.33333" />
- <path id="Vector_2" d="M19.8334 9.33333L14.0001 3.5L8.16675 9.33333" stroke="currentColor"
- stroke-width="2.33333" />
- <path id="Vector_3" d="M14 3.5V17.5" stroke="currentColor" stroke-width="2.33333" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssUploadPPT }}</span>
- </div>
- </FileInput>
- </div>
- <div class="submenu" :class="{ visible: activeSubmenu === 'interactive' }">
- <div class="submenu-title">
- <div class="title">{{ lang.ssAddInteractiveTool }}</div>
- <div class="close-icon" @click="toggleSubmenu('interactive')">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 3">
- <g id="Component 1">
- <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
- </g>
- </g>
- </svg>
- </div>
- </div>
- <transition name="fade" mode="out-in">
- <div class="submenu-panel" v-if="!hoveredTool">
- <svg key="svg" width="120" height="80" viewBox="0 0 120 80" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M102 15H18C13.5817 15 10 18.5817 10 23V57C10 61.4183 13.5817 65 18 65H102C106.418 65 110 61.4183 110 57V23C110 18.5817 106.418 15 102 15Z"
- fill="#FFFAF0" stroke="#FF9300" stroke-width="2" />
- <path id="Vector_2"
- d="M30 34C32.2091 34 34 32.2091 34 30C34 27.7909 32.2091 26 30 26C27.7909 26 26 27.7909 26 30C26 32.2091 27.7909 34 30 34Z"
- fill="#FF9300" />
- <path id="Vector_3"
- d="M97 27H43C41.3431 27 40 28.3431 40 30C40 31.6569 41.3431 33 43 33H97C98.6569 33 100 31.6569 100 30C100 28.3431 98.6569 27 97 27Z"
- fill="#FFD9A8" />
- <path id="Vector_4"
- d="M30 49C32.2091 49 34 47.2091 34 45C34 42.7909 32.2091 41 30 41C27.7909 41 26 42.7909 26 45C26 47.2091 27.7909 49 30 49Z"
- fill="#D1D5DB" />
- <path id="Vector_5"
- d="M82 42H43C41.3431 42 40 43.3431 40 45C40 46.6569 41.3431 48 43 48H82C83.6569 48 85 46.6569 85 45C85 43.3431 83.6569 42 82 42Z"
- fill="#E5E7EB" />
- </g>
- </svg>
- <div class="detail">{{ lang.ssSelectToolCreateInteractive }}</div>
- </div>
- <img class="submenu-img" v-else-if="hoveredTool === 'qa'" key="qa" :src="toolAnswer" alt="">
- <img class="submenu-img" v-else-if="hoveredTool === 'choice'" key="choice" :src="toolChoice" alt="">
- <img class="submenu-img" v-else-if="hoveredTool === 'vote'" key="vote" :src="toolVote" alt="">
- <img class="submenu-img" v-else-if="hoveredTool === 'photo'" key="photo" :src="toolPhoto" alt="">
- <img class="submenu-img" v-else-if="hoveredTool === 'file'" key="file" :src="toolFile" alt="">
- </transition>
- <div class="submenu-item-box">
- <div class="submenu-item" @click="handleToolClick('choice')" @mouseenter="hoveredTool = 'choice'"
- @mouseleave="hoveredTool = null">
- <svg class="submenu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <circle cx="12" cy="12" r="10" />
- <path d="M12 16v-4m0-4h.01" />
- </svg>
- <span class="submenu-label">{{ lang.ssChoiceQ }}</span>
- </div>
- <div class="submenu-item" @click="handleToolClick('qa')" @mouseenter="hoveredTool = 'qa'"
- @mouseleave="hoveredTool = null">
- <svg class="submenu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" />
- </svg>
- <span class="submenu-label">{{ lang.ssQandA }}</span>
- </div>
- <div class="submenu-item" @click="handleToolClick('vote')" @mouseenter="hoveredTool = 'vote'"
- @mouseleave="hoveredTool = null">
- <svg class="submenu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <polyline points="9 11 12 14 22 4"></polyline>
- <path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"></path>
- </svg>
- <span class="submenu-label">{{ lang.ssVote }}</span>
- </div>
- <div class="submenu-item" @click="handleToolClick('photo')" @mouseenter="hoveredTool = 'photo'"
- @mouseleave="hoveredTool = null">
- <svg class="submenu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"></path>
- <circle cx="12" cy="13" r="4"></circle>
- </svg>
- <span class="submenu-label">{{ lang.ssPhoto }}</span>
- </div>
- <div class="submenu-item" @click="handleToolClick('file')" @mouseenter="hoveredTool = 'file'"
- @mouseleave="hoveredTool = null">
- <svg class="submenu-icon" t="1787542984492" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7531" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
- <path d="M239.317333 810.666667l-8.533333-1.28C173.226667 801.152 128 750.805333 128 689.152c0-51.157333 31.146667-94.72 75.050667-112.725333L256.170667 554.666667 256 495.36a213.333333 213.333333 0 0 1 420.437333-51.328l15.189334 61.354667 63.104 3.370666C832.853333 512.853333 896 578.346667 896 659.584c0 84.053333-67.413333 151.04-149.333333 151.04H725.333333v85.333333h21.333334c129.621333 0 234.666667-105.813333 234.666666-236.373333 0-126.293333-98.304-229.461333-222.08-236.074667A298.666667 298.666667 0 0 0 170.666667 495.488v1.962667a206.933333 206.933333 0 0 0-128 191.658666c0 104.192 76.458667 190.421333 175.957333 204.8v2.048H298.666667v-85.333333H239.317333z" fill="currentColor" p-id="7532"></path>
- <path d="M512 512l219.477333 219.477333h-120.704L512 632.618667l-98.858667 98.858666H292.522667L512 512z" fill="currentColor" p-id="7533"></path>
- <path d="M554.666667 597.333333v298.666667h-85.333334v-298.666667h85.333334z" fill="currentColor" p-id="7534"></path>
- </svg>
- <span class="submenu-label">{{ lang.ssFile }}</span>
- </div>
- <div class="submenu-item" @click="handleToolClick('creative')" @mouseenter="hoveredTool = null"
- @mouseleave="hoveredTool = null" v-if="false">
- <svg class="submenu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <circle cx="12" cy="12" r="10" />
- <line x1="12" y1="8" x2="12" y2="16" />
- <line x1="8" y1="12" x2="16" y2="12" />
- </svg>
- <span class="submenu-label">{{ lang.ssCreative }}</span>
- </div>
- </div>
- </div>
- <div class="submenu" :class="{ visible: activeSubmenu === 'aiapp' }">
- <div class="submenu-title">
- <div class="title">{{ lang.ssAddAIApp }}</div>
- <div class="close-icon" @click="toggleSubmenu('aiapp')">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 3">
- <g id="Component 1">
- <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
- </g>
- </g>
- </svg>
- </div>
- </div>
- <div class="submenu-item-box2">
- <div class="submenu-item" @click="handleToolClick('aiapp')">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M18 6H8C6.89543 6 6 6.89543 6 8V18C6 19.1046 6.89543 20 8 20H18C19.1046 20 20 19.1046 20 18V8C20 6.89543 19.1046 6 18 6Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_2"
- d="M40 6H30C28.8954 6 28 6.89543 28 8V18C28 19.1046 28.8954 20 30 20H40C41.1046 20 42 19.1046 42 18V8C42 6.89543 41.1046 6 40 6Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_3"
- d="M40 28H30C28.8954 28 28 28.8954 28 30V40C28 41.1046 28.8954 42 30 42H40C41.1046 42 42 41.1046 42 40V30C42 28.8954 41.1046 28 40 28Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_4"
- d="M18 28H8C6.89543 28 6 28.8954 6 30V40C6 41.1046 6.89543 42 8 42H18C19.1046 42 20 41.1046 20 40V30C20 28.8954 19.1046 28 18 28Z"
- stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssAppCenter }}</span>
- </div>
- <div class="submenu-item" @click="handleToolClick('createApp')">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_2" d="M24 16V32" stroke="currentColor" stroke-width="4" />
- <path id="Vector_3" d="M16 24H32" stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssCreateApp }}</span>
- </div>
- </div>
- </div>
- <div class="submenu" :class="{ visible: activeSubmenu === 'h5page' }">
- <div class="submenu-title">
- <div class="title">{{ lang.ssAddInteractiveWebpage }}</div>
- <div class="close-icon" @click="toggleSubmenu('h5page')">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 3">
- <g id="Component 1">
- <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
- </g>
- </g>
- </svg>
- </div>
- </div>
- <div class="submenu-item-box2">
- <!-- <div class="submenu-item">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M40 6H8C5.79086 6 4 7.79086 4 10V30C4 32.2091 5.79086 34 8 34H40C42.2091 34 44 32.2091 44 30V10C44 7.79086 42.2091 6 40 6Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_2" d="M4 14H44" stroke="currentColor" stroke-width="4" />
- <path id="Vector_3"
- d="M10 11C10.5523 11 11 10.5523 11 10C11 9.44772 10.5523 9 10 9C9.44772 9 9 9.44772 9 10C9 10.5523 9.44772 11 10 11Z"
- fill="currentColor" stroke="currentColor" stroke-width="4" />
- <path id="Vector_4"
- d="M14 11C14.5523 11 15 10.5523 15 10C15 9.44772 14.5523 9 14 9C13.4477 9 13 9.44772 13 10C13 10.5523 13.4477 11 14 11Z"
- fill="currentColor" stroke="currentColor" stroke-width="4" />
- <path id="Vector_5"
- d="M18 11C18.5523 11 19 10.5523 19 10C19 9.44772 18.5523 9 18 9C17.4477 9 17 9.44772 17 10C17 10.5523 17.4477 11 18 11Z"
- fill="currentColor" stroke="currentColor" stroke-width="4" />
- <path id="Vector_6" d="M16 22H32M16 28H26" stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssWebpageCenter }}</span>
- </div> -->
- <div class="submenu-item" @click="handleToolClick('uploadWebpage')">
- <div class="submenu-icon">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <circle cx="12" cy="12" r="10"></circle>
- <path d="M2 12h20"></path>
- <path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"></path>
- <path d="M16 8l-4 4-4-4" stroke-width="1.5"></path>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssUploadWebpageLink }}</span>
- </div>
- <!-- @click="handleToolClick('createWebpage')" -->
- <div class="submenu-item" :class="{ 'loading-state': create_app_loading }" @click="!create_app_loading && handle_add_aiWeb()">
- <div class="submenu-icon" v-if="!create_app_loading">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <rect x="3" y="3" width="18" height="14" rx="2"></rect>
- <path d="M12 8v6"></path>
- <path d="M9 11h6"></path>
- </svg>
- </div>
- <div class="submenu-icon loading-icon" v-else>
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="spin">
- <circle cx="12" cy="12" r="10" stroke-dasharray="32" stroke-dashoffset="12"></circle>
- </svg>
- </div>
- <span class="submenu-label">{{ isExistingWebElement ? lang.ssEditWebpage : create_app_loading ? lang.ssCreating : lang.ssNewWebpage }}</span>
- </div>
- <div class="submenu-item" @click="handleToolClick('uploadCode')">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M42 30V38C42 39.0609 41.5786 40.0783 40.8284 40.8284C40.0783 41.5786 39.0609 42 38 42H10C8.93913 42 7.92172 41.5786 7.17157 40.8284C6.42143 40.0783 6 39.0609 6 38V30"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_2" d="M34 16L24 6L14 16" stroke="currentColor" stroke-width="4" />
- <path id="Vector_3" d="M24 6V30" stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssUploadWebpage }}</span>
- </div>
- <!-- <div class="submenu-item">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_2" d="M4 24H44" stroke="currentColor" stroke-width="4" />
- <path id="Vector_3"
- d="M24 4C29.0026 9.47671 31.8455 16.5841 32 24C31.8455 31.4159 29.0026 38.5233 24 44C18.9974 38.5233 16.1545 31.4159 16 24C16.1545 16.5841 18.9974 9.47671 24 4Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_4" d="M32 16L24 24L16 16" stroke="currentColor" stroke-width="3" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssCrawlWebpage }}</span>
- </div> -->
- </div>
- </div>
- <div class="submenu" :class="{ visible: activeSubmenu === 'uploadWebpage' }">
- <div class="submenu-title">
- <div class="title">{{ lang.ssUploadWebpageLink }}</div>
- <div class="close-icon" @click="activeSubmenu = 'h5page'">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 3">
- <g id="Component 1">
- <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
- </g>
- </g>
- </svg>
- </div>
- </div>
- <div class="line_box">
- <div class="webpage-link-container">
- <h3 class="webpage-link-title">{{ lang.ssWebpageLink }}</h3>
- <input type="text" class="webpage-link-input" :placeholder="lang.ssEnterCompleteUrl" v-model="webpageUrl"
- @input="handleUrlInput" />
- <button class="webpage-link-button"
- :class="{ 'loading': isLoading, 'error': isValidUrl === false, 'disabled': isValidUrl === null || isLoading }"
- :disabled="isValidUrl === null || isLoading || isValidUrl === false" @click="uploadWebpageLink">
- {{ isLoading ? lang.ssUploading : isValidUrl === null ? lang.ssWaitingForInput : isValidUrl === false ?
- lang.ssInvalidUrl : lang.ssStartUpload }}
- </button>
- </div>
- </div>
- </div>
- <div class="submenu" :class="{ visible: activeSubmenu === 'uploadCode' }">
- <div class="submenu-title">
- <div class="title">{{ lang.ssUploadWebpage }}</div>
- <div class="close-icon" @click="activeSubmenu = 'h5page'">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 3">
- <g id="Component 1">
- <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
- </g>
- </g>
- </svg>
- </div>
- </div>
- <div class="line_box">
- <div class="upload-box">
- <div class="upload-tabs">
- <button class="upload-tab" :class="{ active: uploadTab === 'file' }"
- @click="switchUploadTab('file')">{{ lang.ssUploadFile }}</button>
- <button class="upload-tab" :class="{ active: uploadTab === 'code' }"
- @click="switchUploadTab('code')">{{ lang.ssPasteCode }}</button>
- </div>
- <div class="uploadFilePanel" v-if="uploadTab === 'file'">
- <div class="form-group">
- <FileInput accept=".html,.htm" @change="handleFileCodeUpload" v-if="!uploadCodeFile">
- <div class="file-upload-area">
- <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
- <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"></path>
- <polyline points="17 8 12 3 7 8"></polyline>
- <line x1="12" y1="3" x2="12" y2="15"></line>
- </svg>
- <p class="upload-text">{{ lang.ssDragAndDropHint }}</p>
- <p class="upload-hint">{{ lang.ssSupportHTML }}</p>
- </div>
- </FileInput>
- <div class="file-name-display" v-else>
- <span>{{ uploadCodeFile.name }}</span>
- <button type="button" class="upload-task-action"
- @click="handleFileCodeUpload(null)">×</button>
- </div>
- </div>
- </div>
- <div class="pasteCodePanel" v-if="uploadTab === 'code'">
- <div class="form-group">
- <textarea class="code-textarea" :placeholder="lang.ssPasteHTML" v-model="codeInput"></textarea>
- </div>
- </div>
- <button class="webpage-link-button"
- :class="{ 'loading': isLoading, 'disabled': !uploadCodeFile || isLoading }"
- :disabled="!uploadCodeFile || isLoading" @click="handleUploadCodeFile"
- v-if="uploadTab === 'file'">
- {{ isLoading ? lang.ssUploading : !uploadCodeFile ? lang.ssWaitingForInput2 : lang.ssStartUpload }}
- </button>
- <button class="webpage-link-button"
- :class="{ 'loading': isLoading, 'disabled': !codeInput || isLoading }"
- :disabled="!codeInput || isLoading" @click="handleUploadCode"
- v-else-if="uploadTab === 'code'">
- {{ isLoading ? lang.ssUploading : !codeInput ? lang.ssStartUpload : lang.ssStartUpload }}
- </button>
- </div>
- </div>
- </div>
- <div class="submenu" :class="{ visible: activeSubmenu === 'multimedia' }">
- <div class="submenu-title">
- <div class="title">{{ lang.ssAddMultimedia }}</div>
- <div class="close-icon" @click="toggleSubmenu('multimedia')">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 3">
- <g id="Component 1">
- <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
- </g>
- </g>
- </svg>
- </div>
- </div>
- <div class="submenu-item-box2">
- <div class="submenu-item" @click="handleToolClick('video')">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M38 8H10C7.79086 8 6 9.79086 6 12V36C6 38.2091 7.79086 40 10 40H38C40.2091 40 42 38.2091 42 36V12C42 9.79086 40.2091 8 38 8Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_2" d="M20 18L32 24L20 30V18Z" stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssVideo }}</span>
- </div>
- <!-- <div class="submenu-item">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector" d="M18 36V10L42 6V32" stroke="currentColor" stroke-width="4" />
- <path id="Vector_2"
- d="M12 42C15.3137 42 18 39.3137 18 36C18 32.6863 15.3137 30 12 30C8.68629 30 6 32.6863 6 36C6 39.3137 8.68629 42 12 42Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_3"
- d="M36 38C39.3137 38 42 35.3137 42 32C42 28.6863 39.3137 26 36 26C32.6863 26 30 28.6863 30 32C30 35.3137 32.6863 38 36 38Z"
- stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssAudio }}</span>
- </div>
- <div class="submenu-item">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M28 4H12C10.9391 4 9.92172 4.42143 9.17157 5.17157C8.42143 5.92172 8 6.93913 8 8V40C8 41.0609 8.42143 42.0783 9.17157 42.8284C9.92172 43.5786 10.9391 44 12 44H36C37.0609 44 38.0783 43.5786 38.8284 42.8284C39.5786 42.0783 40 41.0609 40 40V16L28 4Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_2" d="M28 4V16H40" stroke="currentColor" stroke-width="4" />
- <path id="Vector_3" d="M16 26H32" stroke="currentColor" stroke-width="4" />
- <path id="Vector_4" d="M16 34H28" stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssDocument }}</span>
- </div>
- <div class="submenu-item">
- <div class="submenu-icon">
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 1">
- <path id="Vector"
- d="M18 8H10C7.79086 8 6 9.79086 6 12V32C6 34.2091 7.79086 36 10 36H18C20.2091 36 22 34.2091 22 32V12C22 9.79086 20.2091 8 18 8Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_2"
- d="M38 8H30C27.7909 8 26 9.79086 26 12V32C26 34.2091 27.7909 36 30 36H38C40.2091 36 42 34.2091 42 32V12C42 9.79086 40.2091 8 38 8Z"
- stroke="currentColor" stroke-width="4" />
- <path id="Vector_3" d="M14 18V26" stroke="currentColor" stroke-width="4" />
- <path id="Vector_4" d="M34 18V26" stroke="currentColor" stroke-width="4" />
- </g>
- </svg>
- </div>
- <span class="submenu-label">{{ lang.ssDocumentSet }}</span>
- </div> -->
- </div>
- </div>
- <div class="submenu submenu-english" :class="{ visible: activeSubmenu === 'english' }">
- <SpeakingPanel />
- </div>
- <div class="submenu" :class="{ visible: activeSubmenu === 'cocoLearn' }">
- <div class="submenu-title">
- <div class="title">{{ lang.ssCrossSubjectCourse }}</div>
- <div class="close-icon" @click="toggleSubmenu('cocoLearn')">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <g id="Component 3">
- <g id="Component 1">
- <path id="Vector" d="M16 18L12 14L16 10" stroke="#9CA3AF" stroke-width="1.33333" />
- </g>
- </g>
- </svg>
- </div>
- </div>
- <div class="submenu-item-box2" v-if="!showKwlDetail && !showSixHatDetail">
- <div class="tool-card kwl-card" @click="handleToolClick('kwl')">
- <div class="tool-banner">
- <img :src="kwl" :alt="lang.ssKwlTitle" />
- <div class="tool-overly"></div>
- <div class="tool-icon-badge">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M9 18h6M10 22h4M12 2a7 7 0 00-4 12.7c.6.5 1 1.3 1 2.3h6c0-1 .4-1.8 1-2.3A7 7 0 0012 2z"></path>
- </svg>
- </div>
- </div>
- <div class="tool-info">
- <div class="tool-title">{{ lang.ssKwlTitle }}</div>
- <div class="tool-desc">{{ lang.ssKwlDesc }}</div>
- <div class="tool-tags">
- <span class="tool-tag">{{ lang.ssKwlGradeRange }}</span>
- </div>
- </div>
- <div class="tool-hover-overlay">
- <span>{{ lang.ssClickToViewDetail }}</span>
- </div>
- </div>
- <div class="tool-card six-hat-card" @click="handleToolClick('sixHat')">
- <div class="tool-banner">
- <img :src="sixHat" :alt="lang.ssSixHatTitle" />
- <div class="tool-overly"></div>
- <div class="tool-icon-badge">
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M9 18h6M10 22h4M12 2a7 7 0 00-4 12.7c.6.5 1 1.3 1 2.3h6c0-1 .4-1.8 1-2.3A7 7 0 0012 2z"></path>
- </svg>
- </div>
- </div>
- <div class="tool-info">
- <div class="tool-title">{{ lang.ssSixHatTitle }}</div>
- <div class="tool-desc">{{ lang.ssSixHatDesc }}</div>
- <div class="tool-tags">
- <span class="tool-tag">{{ lang.ssSixHatGradeRange }}</span>
- </div>
- </div>
- <div class="tool-hover-overlay">
- <span>{{ lang.ssClickToViewDetail }}</span>
- </div>
- </div>
- </div>
- <KwlDetail :banner="kwl" v-else-if="showKwlDetail" @back="showKwlDetail = false" @add="handleKwlAdd" />
- <SixHatDetail v-else @back="showSixHatDetail = false" @add="handleSixHatAdd" />
- </div>
- <AiWeb :visible="showAiWebModal" :web-id="webId" @close="showAiWebModal = false" @add="addAiWeb" :is-loading="isLoading" />
- </div>
- </template>
- <script lang="ts" setup>
- import { ref, watch, computed, useTemplateRef, nextTick, onMounted } from 'vue'
- import { storeToRefs } from 'pinia'
- import useCreateElement from '@/hooks/useCreateElement'
- import useSlideHandler from '@/hooks/useSlideHandler'
- import { useSlidesStore, useMainStore } from '@/store'
- import { useSpeakingStore } from '@/store/speaking'
- import { useArticleReadingStore } from '@/store/articleReading'
- import FileInput from '@/components/FileInput.vue'
- import AiChat from './componets/aiChat.vue'
- import AiWeb from './componets/aiWeb.vue'
- import SpeakingPanel from '@/views/Editor/EnglishSpeaking/SpeakingPanel.vue'
- import { lang } from '@/main'
- import { ARTICLE_READING_TOOL_TYPE } from '@/configs/englishSpeakingTools'
- import toolChoice from '@/assets/img/tool_choice.jpeg'
- import toolAnswer from '@/assets/img/tool_answer.png'
- import toolVote from '@/assets/img/tool_vote.png'
- import toolPhoto from '@/assets/img/tool_photo.png'
- import toolFile from '@/assets/img/tool_file.png'
- import axios from '@/services/config'
- import kwl from '@/assets/img/kwlBanner.jpg'
- import sixHat from '@/assets/img/sixHatBanner.jpg'
- import KwlDetail from './cocolearnCom/KwlDetail.vue'
- import SixHatDetail from './cocolearnCom/SixHatDetail.vue'
- interface ContentItem {
- tool?: number
- title?: string
- url?: string
- id?: string
- }
- const props = withDefaults(defineProps<{
- defaultCollapsed?: boolean
- userid?: string | null
- courseTitle?: string | null
- // 只保留英语口语这一项工具,其余侧边栏入口全部隐藏(EditorEn 展示页用)。
- // 默认 false,index3 等现役编辑器走默认分支,行为不变。
- onlyEnglish?: boolean
- }>(), {
- defaultCollapsed: false,
- userid: null,
- courseTitle: null,
- onlyEnglish: false,
- })
- const getUserInfo = async (userid: string) => {
- const res = await axios.get('https://pbl.cocorobo.cn/api/pbl/selectUser2', {
- params: { userid }
- })
- return res[0][0] || {}
- }
- const userJson = ref<any>({})
- watch(() => props.userid, async (newVal) => {
- if (!newVal) return
- userJson.value = await getUserInfo(newVal)
- })
- const emit = defineEmits<{
- (e: 'toggle', collapsed: boolean): void
- }>()
- const isCollapsed = ref(props.defaultCollapsed)
- // onlyEnglish 时侧边栏只剩英语一项,收着让人先点一下没有意义,直接展开
- const activeSubmenu = ref<string | null>(props.onlyEnglish ? 'english' : null)
- const contentList = ref<ContentItem[]>([])
- const hoveredTool = ref<string | null>(null)
- const showKwlDetail = ref(false)
- const showSixHatDetail = ref(false)
- const webpageUrl = ref('')
- const isLoading = ref(false)
- const isValidUrl = ref<boolean | null>(null) // null: 未输入, true: 有效, false: 无效
- const slidesStore = useSlidesStore()
- const { currentSlide } = storeToRefs(slidesStore)
- const { createFrameElement } = useCreateElement()
- const { createSlide, createSlideByTemplate } = useSlideHandler()
- const mainStore = useMainStore()
- const { running } = storeToRefs(mainStore)
- const aiChatRef = useTemplateRef<HTMLElement>('aiChatRef')
- watch(() => running.value, (newValue) => {
- if (newValue) {
- // 使用 nextTick 确保 DOM 更新后再调用子组件方法
- nextTick(() => {
- toggleSubmenu('cocoai')
- aiChatRef.value.sendQuickAction(lang.ssBatchInteractiveWeb)
- aiChatRef.value.sendMessage()
- })
- mainStore.setRunningState(false)
- }
- })
- import _ from 'lodash'
- const handleUrlInput = _.debounce(() => {
- const url = webpageUrl.value.trim()
- if (!url) {
- isValidUrl.value = null
- }
- else {
- // 简化的URL格式验证,支持各种复杂URL
- const urlRegex = /^https?:\/\/.+/
- isValidUrl.value = urlRegex.test(url)
- }
- console.log('URL输入:', webpageUrl.value, '验证结果:', isValidUrl.value)
- }, 300)
- const uploadWebpageLink = async () => {
- if (!webpageUrl.value || isValidUrl.value !== true) {
- // 可以添加提示信息
- return
- }
- isLoading.value = true
- // 模拟上传过程
- // isLoading.value = false
- // 上传成功后创建iframe元素
- const isValid = await new Promise((resolve) => {
- // 创建隐藏iframe
- const iframe = document.createElement('iframe')
- iframe.style.display = 'none'
- iframe.src = webpageUrl.value
- let finished = false
- const timeout = setTimeout(() => {
- if (finished) return
- finished = true
- // 超时,移除iframe,进入XHR判断
- document.body.removeChild(iframe)
- // 用XHR判断
- const xhr = new XMLHttpRequest()
- xhr.open('GET', iframe.src, true)
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4) {
- if (xhr.status === 200) {
- resolve(true)
- }
- else {
- // 再试一次 getFile
- getFile(iframe.src as any).then(res => {
- if (res && res.data && res.data !== 1) {
- resolve(true)
- }
- else {
- resolve(false)
- }
- }).catch(() => {
- resolve(false)
- })
- }
- }
- }.bind(this)
- xhr.onerror = function() {
- // 再试一次 getFile
- getFile(iframe.src as any).then(res => {
- if (res && res.data && res.data !== 1) {
- resolve(true)
- }
- else {
- resolve(false)
- }
- }).catch(() => {
- resolve(false)
- })
- }.bind(this)
- xhr.send()
- }, 5000)
- iframe.onload = function() {
- if (finished) return
- finished = true
- clearTimeout(timeout)
- try {
- // 尝试访问contentWindow.document
- const doc = iframe?.contentWindow?.document
- document.body.removeChild(iframe)
- resolve(true)
- }
- catch (e) {
- // 跨域或其他异常,移除iframe,进入XHR判断
- document.body.removeChild(iframe)
- const xhr = new XMLHttpRequest()
- xhr.open('GET', iframe.src, true)
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4) {
- if (xhr.status === 200) {
- resolve(true)
- }
- else {
- // 再试一次 getFile
- getFile(iframe.src as any).then(res => {
- if (res && res.data && res.data !== 1) {
- resolve(true)
- }
- else {
- resolve(false)
- }
- }).catch(() => {
- resolve(false)
- })
- }
- }
- }.bind(this)
- xhr.onerror = function() {
- // 再试一次 getFile
- getFile(iframe.src as any).then(res => {
- if (res && res.data && res.data !== 1) {
- resolve(true)
- }
- else {
- resolve(false)
- }
- }).catch(() => {
- resolve(false)
- })
- }.bind(this)
- xhr.send()
- }
- }
- iframe.onerror = function() {
- if (finished) return
- finished = true
- clearTimeout(timeout)
- document.body.removeChild(iframe)
- // iframe加载失败,进入XHR判断
- const xhr = new XMLHttpRequest()
- xhr.open('GET', iframe.src, true)
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4) {
- if (xhr.status === 200) {
- resolve(true)
- }
- else {
- // 再试一次 getFile
- getFile(iframe.src as any).then(res => {
- if (res && res.data && res.data !== 1) {
- resolve(true)
- }
- else {
- resolve(false)
- }
- }).catch(() => {
- resolve(false)
- })
- }
- }
- }.bind(this)
- xhr.onerror = function() {
- // 再试一次 getFile
- getFile(iframe.src as any).then(res => {
- if (res && res.data && res.data !== 1) {
- resolve(true)
- }
- else {
- resolve(false)
- }
- }).catch(() => {
- resolve(false)
- })
- }.bind(this)
- xhr.send()
- }
- document.body.appendChild(iframe)
- })
- if (!isValid) {
- // message.error(lang.ssCocoLinkTip)
- message.warning(lang.ssCocoLinkTip2)
- createSlide()
- createFrameElement(webpageUrl.value, 81) // 假设15是网页工具的类型
- isLoading.value = false
- // 清空输入框和验证状态
- webpageUrl.value = ''
- isValidUrl.value = null
- return
- }
- isLoading.value = false
- createSlide()
- createFrameElement(webpageUrl.value, 73) // 假设15是网页工具的类型
- // 清空输入框和验证状态
- webpageUrl.value = ''
- isValidUrl.value = null
- }
- const toggleCollapse = () => {
- isCollapsed.value = !isCollapsed.value
- emit('toggle', isCollapsed.value)
- }
- const toggleSubmenu = (menu: string) => {
- if (activeSubmenu.value === menu) {
- activeSubmenu.value = null
- if (menu === 'cocoLearn') {
- showKwlDetail.value = false
- }
- }
- else {
- activeSubmenu.value = menu
- }
- }
- // 监听"打开口语配置面板"信号 → 自动展开 english 子菜单
- const speakingStore = useSpeakingStore()
- const articleStore = useArticleReadingStore()
- function openEnglishSubmenu() {
- if (isCollapsed.value) {
- isCollapsed.value = false
- emit('toggle', false)
- }
- activeSubmenu.value = 'english'
- }
- watch(() => speakingStore.openConfigSignal, (val, old) => {
- if (val !== old) openEnglishSubmenu()
- })
- // 文章朗读(772 型)有自己的 store 与信号,为的是两条配置页互不干扰 —— 但**抽屉是共用的**。
- // 少了这一条,点画布上的文章朗读预览只切了 SpeakingPanel 内部的 pageMode,侧栏还收着或停在
- // 别的子菜单,老师眼里就是「点了没反应」。
- watch(() => articleStore.openConfigSignal, (val, old) => {
- if (val !== old) openEnglishSubmenu()
- })
- import titlePage from './page/TitlePage.json'
- import ImagePage from './page/ImagePage.json'
- import ContentPage from './page/ContentPage.json'
- import ImageTextPage from './page/ImageTextPage.json'
- const handleToolClick = _.debounce((tool: string) => {
- interface ParentWindowWithToolList extends Window {
- addTool?: (id: number) => void;
- openVideoUploadDialog?: () => void;
- openApplicationCenter?: () => void;
- }
- const parentWindow = window.parent as ParentWindowWithToolList
- console.log('点击工具:', tool)
- if (tool === 'h5page') {
- parentWindow?.addTool?.(73)
- }
- else if (tool === 'aiapp') {
- parentWindow?.addTool?.(72)
- }
- else if (tool === 'createApp') {
- if (lang.lang === 'cn') {
- window.open('https://cloud.cocorobo.cn/admin.html?type=cocoflow1', '_blank')
- }
- else if (lang.lang === 'en') {
- window.open('https://cloud.cocorobo.com/admin.html?type=cocoflow1', '_blank')
- }
- else if (lang.lang === 'hk') {
- window.open('https://cloud.cocorobo.hk/admin.html?type=cocoflow1', '_blank')
- }
- }
- else if (tool === 'video') {
- parentWindow?.openVideoUploadDialog?.()
- }
- else if (tool === 'creative') {
- parentWindow?.openApplicationCenter?.()
- }
- else if (tool === 'choice') {
- parentWindow?.addTool?.(45)
- }
- else if (tool === 'vote') {
- parentWindow?.addTool?.(78)
- }
- else if (tool === 'photo') {
- parentWindow?.addTool?.(79)
- }
- else if (tool === 'file') {
- parentWindow?.addTool?.(84)
- }
- else if (tool === 'qa') {
- parentWindow?.addTool?.(15)
- }
- else if (tool === 'titlepage') {
- createSlideByTemplate(titlePage)
- }
- else if (tool === 'ImagePage') {
- createSlideByTemplate(ImagePage)
- }
- else if (tool === 'ContentPage') {
- createSlideByTemplate(ContentPage)
- }
- else if (tool === 'ImageTextPage') {
- createSlideByTemplate(ImageTextPage)
- }
- else if (tool === 'createWebpage') {
- if (lang.lang === 'cn') {
- window.open('https://cloud.cocorobo.cn/admin.html?type=cocoflow3', '_blank')
- }
- else if (lang.lang === 'en') {
- window.open('https://cloud.cocorobo.com/admin.html?type=cocoflow3', '_blank')
- }
- else if (lang.lang === 'hk') {
- window.open('https://cloud.cocorobo.hk/admin.html?type=cocoflow3', '_blank')
- }
- }
- else if (tool === 'uploadWebpage') {
- activeSubmenu.value = 'uploadWebpage'
- }
- else if (tool === 'uploadCode') {
- activeSubmenu.value = 'uploadCode'
- }
- else if (tool === 'kwl') {
- showKwlDetail.value = true
- }
- else if (tool === 'sixHat') {
- showSixHatDetail.value = true
- }
- }, 300)
- const handleKwlAdd = (data: any) => {
- interface ParentWindowWithToolList extends Window {
- addTool2?: (id: number, data: any) => void;
- }
- const parentWindow = window.parent as ParentWindowWithToolList
- // parentWindow?.addTool?.(80)
- // console.log('KWL配置:', data)
- parentWindow?.addTool2?.(831, data)
- }
- const handleSixHatAdd = (data: any) => {
- interface ParentWindowWithToolList extends Window {
- addTool2?: (id: number, data: any) => void;
- }
- const parentWindow = window.parent as ParentWindowWithToolList
- parentWindow?.addTool2?.(832, data)
- }
- const loadContentList = () => {
- try {
- interface ParentWindowWithToolList extends Window {
- pptToolList?: ContentItem[]
- }
- const parentWindow = window.parent as ParentWindowWithToolList
- contentList.value = parentWindow?.pptToolList || []
- }
- catch (error) {
- console.error('加载内容列表失败:', error)
- contentList.value = []
- }
- }
- const insertContent = (item: ContentItem) => {
- if (!item.tool || !item.url) return
- createFrameElement(item.url, item.tool)
- }
- const toolTypeArray = ref([45, 15, 78, 79, 84, 831, 832])
- const addContent = (data: ContentItem, type: number) => {
- // contentList.value.push(data)
- if (type === 2) {
- const elements = currentSlide.value?.elements || []
- const frameElement = elements.find((el: any) => el.type === 'frame' && toolTypeArray.value.includes(el.toolType))
- if (frameElement) {
- slidesStore.updateElement({
- id: frameElement.id,
- props: { url: data.url, toolType: data.tool }
- })
- }
- }
- else {
- createSlide()
- insertContent(data)
- }
- }
- Object.assign(window, { addContent, loadContentList })
- // window.loadContentList = loadContentList
- // window.addContent = addContent
- const previewVideo = (item: ContentItem) => {
- interface ParentWindowWithToolList extends Window {
- previewVideo?: (item: ContentItem) => void;
- }
- const parentWindow = window.parent as ParentWindowWithToolList
- parentWindow?.previewVideo?.(item)
- }
- const editContent = (item: ContentItem) => {
- interface ParentWindowWithToolList extends Window {
- toolBtn?: (action: number, id: string) => void;
- }
- const parentWindow = window.parent as ParentWindowWithToolList
- parentWindow?.toolBtn?.(0, item.id || '')
- }
- const copyContent = (item: ContentItem) => {
- interface ParentWindowWithToolList extends Window {
- toolBtn?: (action: number, id: string) => void;
- }
- const parentWindow = window.parent as ParentWindowWithToolList
- parentWindow?.toolBtn?.(1, item.id || '')
- }
- const deleteContent = (item: ContentItem) => {
- interface ParentWindowWithToolList extends Window {
- toolBtn?: (action: number, id: string) => void;
- }
- const parentWindow = window.parent as ParentWindowWithToolList
- parentWindow?.toolBtn?.(2, item.id || '')
- }
- const getTypeLabel = (type?: number) => {
- const typeMap: Record<number, string> = {
- 45: lang.ssChoiceQ,
- 15: lang.ssQATest,
- 72: lang.ssAiApp,
- 73: lang.ssHPage,
- 81: lang.ssHPage,
- 74: lang.ssVideo,
- 75: lang.lang == 'cn' ? lang.ssBiliVideo : lang.ssYouTube,
- 76: lang.ssCreative,
- 77: lang.ssEnglishSpeakingTool,
- [ARTICLE_READING_TOOL_TYPE]: lang.ssArticleReadingTool,
- 78: lang.ssVote,
- 79: lang.ssPhoto,
- 84: lang.ssFile,
- 831: lang.ssKwlTitle,
- 832: lang.ssSixHatTitle,
- }
- return typeMap[type || 0] || lang.ssUnknown
- }
- const getTypeClass = (type?: number) => {
- const classMap: Record<number, string> = {
- 45: 'type-choice',
- 15: 'type-question',
- 72: 'type-ai',
- 73: 'type-h5',
- 74: 'type-video',
- 75: 'type-bilibili',
- 76: 'type-app-center',
- 831: 'type-kwl',
- 832: 'type-six-hat',
- 84: 'type-file',
- }
- return classMap[type || 0] || 'type-default'
- }
- import useImport from '@/hooks/useImport'
- import message from '@/utils/message'
- const { importPPTXFile, exporting, getFile, getPPTInfo, uploadFileToS3 } = useImport()
- const currentFileName = ref('')
- const currentFileSize = ref(0)
- const parsingStatus = ref<'parsing' | 'success'>('parsing')
- const parsingAbortController = ref<AbortController | null>(null)
- const showFileConfirmModal = ref(false)
- const pendingFile = ref<FileList | null>(null)
- const selectedImportOption = ref<'page' | 'library'>('page')
- const pageCount = ref(1)
- const readingFile = ref(false)
- const importProgress = ref(0)
- const progressInterval = ref<NodeJS.Timeout | null>(null)
- const exportingDialog = ref(false)
- // 格式化文件大小
- const formatFileSize = (bytes: number): string => {
- if (bytes === 0) return '0 B'
- const k = 1024
- const sizes = ['B', 'KB', 'MB', 'GB']
- const i = Math.floor(Math.log(bytes) / Math.log(k))
- return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
- }
- const handleFileUpload = async (files: FileList) => {
- if (!files || files.length === 0) return
- const file = files[0]
- currentFileName.value = file.name
- currentFileSize.value = file.size
- pendingFile.value = files
- selectedImportOption.value = 'page'
- readingFile.value = true
- try {
- // 读取 PPT 信息获取页码
- const info = await getPPTInfo(file)
- pageCount.value = info.pageCount
- }
- catch (error) {
- console.error('获取 PPT 信息失败:', error)
- pageCount.value = 1 // 失败时默认显示 1 页
- }
- finally {
- readingFile.value = false
- }
- showFileConfirmModal.value = true
- }
- const confirmFileUpload = async () => {
- if (!pendingFile.value) return
- showFileConfirmModal.value = false
- importProgress.value = 0
- exportingDialog.value = true
- const startTimer = () => {
- // 启动虚拟进度条(30秒从0-99%)
- if (progressInterval.value) clearInterval(progressInterval.value)
- const startTime = Date.now()
- const duration = 30000 // 30秒
- progressInterval.value = setInterval(() => {
- console.log('progressInterval.value', progressInterval.value)
- const elapsed = Date.now() - startTime
- const progress = Math.min(99, Math.floor((elapsed / duration) * 99))
- importProgress.value = progress
- if (progress >= 99) {
- importProgress.value = 99
- }
- if (!exporting.value) {
- if (progressInterval.value) {
- clearInterval(progressInterval.value)
- importProgress.value = 100
- progressInterval.value = null
- setTimeout(() => {
- handleParsingClose()
- }, 2000)
- }
- }
- }, 100)
- }
- const confirmOnclose = () => {
- handleParsingClose()
- }
- try {
- // 创建AbortController用于取消操作
- parsingAbortController.value = new AbortController()
- const signal = parsingAbortController.value.signal
- // 调用importPPTXFile并传入signal
- await importPPTXFile(pendingFile.value, { signal, startTimer, confirmOnclose })
- }
- catch (error) {
- if (error instanceof DOMException && error.name === 'AbortError') {
- console.log(lang.ssFileParseCancelled)
- }
- else {
- console.error(lang.ssFileParseFailed, error)
- message.error(lang.ssFileParseFailedRetry)
- }
- }
- finally {
- // if (progressInterval.value) {
- // clearInterval(progressInterval.value)
- // progressInterval.value = null
- // importProgress.value = 100
- // }
- pendingFile.value = null
- }
- }
- const cancelFileUpload = () => {
- showFileConfirmModal.value = false
- pendingFile.value = null
- }
- const handleParsingClose = () => {
- if (progressInterval.value) {
- clearInterval(progressInterval.value)
- progressInterval.value = null
- }
- if (exporting.value && parsingAbortController.value) {
- parsingAbortController.value.abort()
- exporting.value = false
- parsingAbortController.value = null
- // message.info(lang.ssParseCancelled)
- }
- else if (!exporting.value) {
- emit('close')
- }
- exportingDialog.value = false
- }
- const uploadTab = ref('file')
- const switchUploadTab = (tab: 'file' | 'code') => {
- uploadTab.value = tab
- }
- const codeInput = ref('')
- const uploadCodeFile = ref<File | null>()
- const handleFileCodeUpload = (files: FileList | null) => {
-
- if (!files || files.length === 0) {
- uploadCodeFile.value = null
- return
- }
- const file = files[0]
- uploadCodeFile.value = file
- }
- const handleUploadCodeFile = async () => {
- if (!uploadCodeFile.value) return
- const fileName = uploadCodeFile.value.name.toLowerCase()
- if (fileName.endsWith('.html') || fileName.endsWith('.htm')) {
- isLoading.value = true
- const url = await uploadFileToS3(uploadCodeFile.value)
- createSlide()
- createFrameElement(url, 73)
- uploadCodeFile.value = null
- isLoading.value = false
- }
- }
- const handleUploadCode = async () => {
- if (!codeInput.value) return
- const file = new File([codeInput.value], 'index.html', { type: 'text/html' })
- isLoading.value = true
- const url = await uploadFileToS3(file)
- createSlide()
- createFrameElement(url, 73)
- codeInput.value = ''
- isLoading.value = false
- }
- const isExistingWebElement = computed(() => {
- const elements = currentSlide.value?.elements || []
- const existingWebElement = elements.find((el: any) => el.toolType === 73 && el.webId && el.type === 'frame')
- return existingWebElement && existingWebElement.webId
- })
- // AI Web 相关状态
- const create_app_loading = ref(false)
- const webId = ref('')
- const showAiWebModal = ref(false)
- const isEdit = ref(false)
- const handle_add_aiWeb = _.throttle(async () => {
- create_app_loading.value = true
- try {
- const elements = currentSlide.value?.elements || []
- console.log(elements)
- const existingWebElement = elements.find((el: any) => el.toolType === 73 && el.webId && el.type === 'frame')
-
- if (existingWebElement && existingWebElement.webId) {
- webId.value = existingWebElement.webId
- create_app_loading.value = false
- showAiWebModal.value = true
- isEdit.value = true
- return
- }
- const res = await axios.post('https://appapi.cocorobo.cn/api/agents/ai_edit', {
- userid: props.userid || '',
- username: userJson.value.name || '',
- muti_name: lang.ssAddWebH5,
- description: lang.ssAddWebH5,
- headUrl: 'https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/default%2F%E9%BB%98%E8%AE%A4%E5%A4%B4%E5%83%8F51741770599274.svg',
- organizeid: userJson.value.organizeid || '',
- content: ''
- })
- console.log(res)
- webId.value = res.id || ''
- create_app_loading.value = false
- showAiWebModal.value = true
- }
- catch (error) {
- console.log(error)
- create_app_loading.value = false
- }
- }, 3000)
- const addAiWeb = async (code: string) => {
- if (!code) return
- const file = new File([code], 'index.html', { type: 'text/html' })
- isLoading.value = true
- const url = await uploadFileToS3(file)
- if (isEdit.value) {
- const elements = currentSlide.value?.elements || []
- const frameElement = elements.find((el: any) => el.type === 'frame' && (el.toolType === 73))
- if (frameElement) {
- slidesStore.updateElement({
- id: frameElement.id,
- props: { url: url }
- })
- }
- }
- else {
- createSlide()
- createFrameElement(url, 73, webId.value)
- }
-
- isLoading.value = false
- showAiWebModal.value = false
- webId.value = ''
- isEdit.value = false
- }
- onMounted(async () => {
- if (!props.userid) return
- userJson.value = await getUserInfo(props.userid)
- })
- </script>
- <style lang="scss" scoped>
- .collapsible-toolbar {
- width: 100%;
- height: 100%;
- display: flex;
- background: #fff;
- border-right: 1px solid #e5e7eb;
- transition: width 0.3s ease;
- z-index: 1000;
- }
- .toolbar-content {
- flex: 1;
- overflow: hidden;
- padding: 16px 8px;
- }
- .sidebar-content {
- display: flex;
- flex-direction: column;
- gap: 6px;
- width: 84px;
- position: relative;
- }
- .sidebar-divider {
- position: relative;
- margin: 4px 2px 2px;
- height: 12px;
- }
- .sidebar-divider::before {
- content: "";
- position: absolute;
- left: 0;
- right: 0;
- top: 50%;
- height: 1px;
- background: #e8ddd0;
- }
- .feature-sidebar-item {
- min-height: 82px;
- padding-top: 12px;
- padding-bottom: 12px;
- gap: 7px;
- border: 1px solid #f1e2cc;
- background: #fffdfa;
- box-shadow: 0 8px 18px rgba(15, 23, 42, 0.05);
- &:hover {
- box-shadow: #f78b221f 0px 12px 24px !important;
- background: #fff8ef !important;
- border-color: #f78b2252 !important;
- }
- &.active {
- box-shadow: #f78b222e 0px 14px 28px !important;
- background: #fff2df !important;
- border-color: #f78b2266 !important;
- }
- }
- .sidebar-item {
- width: 84px;
- padding: 12px 8px;
- border-radius: 12px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 6px;
- cursor: pointer;
- transition: all 0.2s;
- position: relative;
- &:hover {
- background: #f3f4f6;
- }
- &:active {
- background: #e5e7eb;
- }
- &.active {
- background: #fff5e5;
- box-shadow: 0 2px 8px rgba(40, 92, 245, 0.15);
- }
- &.active::after {
- content: '';
- position: absolute;
- left: -8px;
- top: 50%;
- transform: translateY(-50%);
- width: 4px;
- height: 32px;
- background: #FF9300;
- border-radius: 0 2px 2px 0;
- }
- }
- .item-icon {
- width: 22px;
- height: 22px;
- flex-shrink: 0;
- color: #6b7280;
- }
- .sidebar-item:hover .item-icon,
- .sidebar-item.active .item-icon {
- color: #FF9300;
- }
- .item-label {
- font-size: 11px;
- font-weight: 500;
- color: #6b7280;
- text-align: center;
- }
- .sidebar-item:hover .item-label,
- .sidebar-item.active .item-label {
- color: #FF9300;
- font-weight: 600;
- }
- .submenu {
- width: 0;
- min-width: 0;
- overflow: hidden;
- // transition: all 0.3s ease;
- background: #fff;
- border-radius: 0 12px 12px 0;
- z-index: 100;
- &.visible {
- width: 420px;
- // padding: 16px;
- border-left: 1px solid #E5E7EB;
- }
- @media screen and (max-width: 1920px) {
- &.visible {
- width: 360px;
- }
- }
- @media screen and (max-width: 1440px) {
- &.visible {
- width: 320px;
- }
- }
- &.submenu-english {
- display: flex;
- flex-direction: column;
- }
- }
- .submenu-title {
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid #f0f0f0;
- margin-bottom: 20px;
- width: 100%;
- box-sizing: border-box;
- padding: 12px 15px;
- .title {
- font-size: 14px;
- font-weight: 600;
- color: #333;
- }
- .close-icon {
- width: 28px;
- height: 28px;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 8px;
- cursor: pointer;
- transition: all 0.2s ease;
- &:hover {
- background-color: #f3f4f6;
- svg {
- stroke: #6b7280;
- }
- }
- svg {
- width: 25px;
- height: 25px;
- stroke: #9ca3af;
- transition: all 0.2s ease;
- }
- }
- }
- .submenu-content {
- width: 100%;
- height: calc(100% - 50px);
- box-sizing: border-box;
- overflow: hidden;
- }
- .submenu-img {
- width: calc(100% - 30px);
- margin: 0 auto;
- height: 130px;
- display: flex;
- object-fit: cover;
- border-radius: 10px;
- margin-bottom: 20px;
- }
- .submenu-panel {
- width: calc(100% - 30px);
- height: 130px;
- margin: 0 auto;
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 15px 0;
- margin-bottom: 20px;
- background: #FFFAF0;
- border-radius: 10px;
- box-sizing: border-box;
- svg,
- img {
- width: 120px;
- height: auto;
- object-fit: cover;
- }
- .detail {
- font-size: 12px;
- font-weight: 500;
- color: #6B7280;
- text-align: center;
- margin-top: 5px;
- }
- }
- /* 淡入淡出过渡效果 */
- .fade-enter-active,
- .fade-leave-active {
- transition: opacity 0.3s ease, transform 0.3s ease;
- }
- .fade-enter-from,
- .fade-leave-to {
- opacity: 0;
- transform: scale(0.95);
- }
- .fade-enter-to,
- .fade-leave-from {
- opacity: 1;
- transform: scale(1);
- }
- .submenu-item-box {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 12px;
- padding: 0 15px;
- .submenu-item {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px;
- cursor: pointer;
- transition: all 0.2s ease;
- font-size: 14px;
- color: #333;
- border-radius: 12px;
- background: #fff;
- border: 1px solid #e5e7eb;
- &:hover {
- background-color: #fffdfa;
- border-color: #fffdfa;
- color: #FF9300;
- transform: translateY(-1px);
- box-shadow: 0 2px 8px rgba(40, 92, 245, 0.1);
- }
- &:active {
- background-color: #fffdfa;
- transform: translateY(0);
- }
- }
- .submenu-icon {
- width: 20px;
- height: 20px;
- flex-shrink: 0;
- color: #6b7280;
- background: #fffaf0;
- border-radius: 10px;
- padding: 8px;
- border: 1px solid #e5e7eb;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .submenu-item:hover .submenu-icon {
- color: #FF9300;
- border-color: #FF9300;
- box-shadow: 0 0 0 2px rgba(255, 147, 0, 0.1);
- }
- .submenu-label {
- font-size: 14px;
- font-weight: 500;
- color: #333;
- flex: 1;
- }
- .submenu-item:hover .submenu-label {
- color: #FF9300;
- font-weight: 600;
- }
- }
- .submenu-item-box2 {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 12px;
- padding: 0 15px;
- .submenu-item {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px;
- cursor: pointer;
- transition: all 0.2s ease;
- font-size: 14px;
- color: #333;
- border-radius: 12px;
- border: 1px solid #e5e7eb;
- flex-direction: column;
- background: #fafbfc;
- &:hover {
- background-color: #fffdfa;
- border-color: #fffdfa;
- color: #FF9300;
- transform: translateY(-1px);
- box-shadow: 0 2px 8px rgba(40, 92, 245, 0.1);
- }
- &:active {
- background-color: #fffdfa;
- transform: translateY(0);
- }
- }
- .submenu-icon {
- flex-shrink: 0;
- color: #6b7280;
- background: #fff;
- border-radius: 10px;
- padding: 25px;
- border: 1px solid #e5e7eb;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 95%;
- svg {
- width: 40px;
- height: 40px;
- color: #d7dbe0;
- }
- }
- .submenu-item:hover .submenu-icon {
- svg {
- color: #FF9300;
- }
- border-color: #FF9300;
- box-shadow: 0 0 0 2px rgba(255, 147, 0, 0.1);
- }
- .submenu-label {
- font-size: 14px;
- font-weight: 600;
- color: #333;
- flex: 1;
- }
- .submenu-item:hover .submenu-label {
- color: #FF9300;
- font-weight: 600;
- }
- }
- .tool-card {
- width: 100%;
- border-radius: 12px;
- overflow: hidden;
- background: #fff;
- border: 1px solid #e5e7eb;
- cursor: pointer;
- transition: all 0.25s ease;
- position: relative;
- &:hover {
- border-color: #FF9300;
- box-shadow: 0 4px 16px rgba(255, 147, 0, 0.15);
- transform: translateY(-2px);
- .tool-hover-overlay {
- opacity: 1;
- }
- .tool-info .tool-title {
- color: #FF9300;
- }
- }
- .tool-banner {
- position: relative;
- width: 100%;
- padding-top: 65%;
- overflow: hidden;
- .tool-overly {
- position: absolute;
- inset: 0;
- background: rgba(37, 99, 235, 0.15);
- opacity: .45;
- pointer-events: none;
- }
- img {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- object-fit: cover;
- transition: transform 0.3s ease;
- }
- .tool-icon-badge {
- position: absolute;
- top: 10px;
- left: 10px;
- width: 32px;
- height: 32px;
- border-radius: 8px;
- background: rgba(255, 255, 255, 0.95);
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
- svg {
- width: 18px;
- height: 18px;
- // color: #FF9300;
- }
- }
- }
- .tool-info {
- padding: 12px 14px 14px;
- .tool-title {
- font-size: 15px;
- font-weight: 600;
- color: #1f2937;
- margin-bottom: 4px;
- transition: color 0.2s ease;
- }
- .tool-desc {
- font-size: 12px;
- color: #6b7280;
- line-height: 1.5;
- margin-bottom: 10px;
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- }
- .tool-tags {
- display: flex;
- gap: 6px;
- }
- .tool-tag {
- display: inline-block;
- padding: 3px 10px;
- font-size: 11px;
- color: #3b82f6;
- background: #eff6ff;
- border-radius: 4px;
- font-weight: 500;
- }
- }
- .tool-hover-overlay {
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- top: 0;
- background: rgba(0, 0, 0, 0.45);
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0;
- transition: opacity 0.25s ease;
- span {
- color: #fff;
- font-size: 14px;
- font-weight: 600;
- }
- }
- }
- // KWL 专属样式(目前无额外覆盖,预留扩展)
- .kwl-card {
- // 如需 KWL 专属样式,在此处覆盖 .tool-card 中的对应规则
- .tool-icon-badge svg{
- color: #2563eb;
- }
- .tool-overly {
- background-color: #2563eb;
- }
- }
- // 六顶思考帽专属样式(琥珀色主题)
- .six-hat-card {
- .tool-icon-badge svg{
- color: #f59e0b;
- }
- .tool-overly {
- background-color: #f59e0b;
- }
- }
- .submenu-upload {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px;
- cursor: pointer;
- transition: all 0.2s ease;
- font-size: 14px;
- color: #333;
- border-radius: 12px;
- border: 1px solid #e5e7eb;
- background: #fafbfc;
- width: calc(100% - 30px);
- margin: 10px auto 0;
- justify-content: center;
- .submenu-icon {
- width: 20px;
- height: 20px;
- svg {
- color: #d7dbe0;
- width: 20px;
- height: 20px;
- }
- }
- .submenu-label {
- font-size: 14px;
- font-weight: 600;
- color: #333;
- }
- &:hover {
- background-color: #fffdfa;
- border-color: #fffdfa;
- color: #FF9300;
- transform: translateY(-1px);
- box-shadow: 0 2px 8px rgba(40, 92, 245, 0.1);
- .submenu-icon {
- svg {
- color: #FF9300;
- }
- }
- .submenu-label {
- color: #FF9300;
- }
- }
- }
- .progress-inline {
- display: flex;
- flex-direction: column;
- gap: 12px;
- padding: 14px 14px 12px;
- border-radius: 16px;
- background: #fff;
- border: 1px solid #f0ebe3;
- box-shadow: 0 8px 20px rgba(15, 23, 42, 0.04);
- width: calc(100% - 30px);
- margin: 15px auto;
- .progress-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12px;
- // .file-name {
- // font-size: 14px;
- // font-weight: 600;
- // color: #333;
- // }
- // .progress-percent {
- // font-size: 14px;
- // font-weight: 600;
- // color: #FF9300;
- // }
- .upload-task-main {
- min-width: 0;
- }
- .upload-task-name {
- font-size: 13px;
- font-weight: 700;
- color: #111827;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .upload-task-meta {
- margin-top: 2px;
- font-size: 11px;
- color: #8b7356;
- }
- .upload-task-side {
- display: flex;
- align-items: center;
- gap: 8px;
- flex-shrink: 0;
- }
- .upload-task-percent {
- font-size: 12px;
- font-weight: 700;
- color: #c76a0c;
- min-width: 40px;
- text-align: right;
- }
- .upload-task-action {
- border: 0;
- background: transparent;
- color: #9a8a77;
- font-size: 12px;
- font-weight: 600;
- cursor: pointer;
- }
- .upload-task-close {
- width: 20px;
- height: 20px;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- border-radius: 999px;
- color: #8b7356;
- font-size: 16px;
- line-height: 1;
- }
- }
- .progress-bar {
- width: 100%;
- height: 4px;
- background: #f0f0f0;
- border-radius: 4px;
- overflow: hidden;
- .progress-fill {
- height: 100%;
- background: linear-gradient(90deg, #f9b24e 0%, #f07815 100%);
- transition: width 0.24s ease;
- border-radius: 4px;
- transition: width 0.1s ease;
- }
- }
- .progress-loading {
- font-size: 12px;
- color: #111827;
- }
- .close-btn {
- background: #FF9300;
- color: white;
- border: none;
- border-radius: 8px;
- padding: 10px 20px;
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- width: 100%;
- transition: all 0.3s;
- &:hover {
- background: #e68a00;
- }
- }
- }
- .line_box {
- .webpage-link-container {
- padding: 20px;
- // text-align: center;
- .webpage-link-title {
- margin: 0 0 16px;
- font-size: 14px;
- font-weight: 600;
- color: #333;
- }
- .webpage-link-input {
- width: 100%;
- padding: 12px 12px;
- border: 1px solid #d9d9d9;
- border-radius: 8px;
- font-size: 14px;
- margin-bottom: 16px;
- transition: all 0.3s;
- box-sizing: border-box;
- &:focus {
- outline: none;
- border-color: #FF9300;
- background: #fff8f0;
- }
- }
- .webpage-link-button {
- text-align: center;
- padding: 10px 24px;
- border: none;
- border-radius: 4px;
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- transition: all 0.3s;
- background-color: #FF9300;
- color: white;
- margin: 0 auto;
- display: block;
- &:hover:not(:disabled) {
- background-color: #e68a00;
- }
- &:disabled {
- opacity: 0.5;
- cursor: not-allowed;
- }
- &.loading {
- cursor: not-allowed;
- opacity: 0.8;
- }
- &.error {
- background-color: #ff4d4f;
- color: white;
- &:hover:not(:disabled) {
- background-color: #ff7875;
- }
- }
- }
- }
- }
- .upload-dropzone {
- position: relative;
- border: 1.5px dashed rgba(247, 139, 34, 0.38);
- border-radius: 20px;
- background: #fffaf4;
- min-height: 156px;
- padding: 22px 20px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- text-align: center;
- gap: 10px;
- cursor: pointer;
- transition: all 0.22s ease;
- overflow: hidden;
- width: calc(100% - 30px);
- margin: 15px auto;
- box-sizing: border-box;
- &:hover {
- border-style: solid;
- border-color: #f78b22;
- background: #fff3e2;
- box-shadow: 0 18px 34px rgba(247, 139, 34, 0.16);
- transform: translateY(-1px);
- }
- .upload-dropzone-icon {
- width: 56px;
- height: 56px;
- border-radius: 18px;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #f78b22;
- background: rgba(247, 139, 34, 0.10);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
- svg {
- width: 28px;
- height: 28px;
- }
- }
- .upload-dropzone-title {
- font-size: 16px;
- font-weight: 700;
- color: #111827;
- }
- .upload-dropzone-subtitle {
- font-size: 12px;
- line-height: 1.6;
- color: #7c6d5d;
- max-width: 320px;
- }
- .upload-dropzone-footnote {
- font-size: 11px;
- color: #9a8a77;
- }
- }
- .reading-file {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 40px 20px;
- gap: 16px;
- .loading-spinner {
- width: 40px;
- height: 40px;
- border: 4px solid #f0f0f0;
- border-top: 4px solid #FF9300;
- border-radius: 50%;
- animation: spin 1s linear infinite;
- }
- .reading-text {
- font-size: 14px;
- color: #666;
- }
- }
- @keyframes spin {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
- }
- .file-confirm-inline {
- width: calc(100% - 30px);
- margin: 15px auto;
- box-sizing: border-box;
- margin-top: 18px;
- padding: 18px;
- border-radius: 18px;
- background: #fff;
- border: 1px solid #f3e4cf;
- box-shadow: 0 12px 28px rgba(15, 23, 42, 0.06);
- .file-info {
- margin-bottom: 16px;
- .file-title {
- font-size: 14px;
- font-weight: 700;
- color: #111827;
- margin-bottom: 6px;
- }
- .file-subtitle {
- font-size: 12px;
- color: #7c6d5d;
- }
- }
- .import-options {
- display: flex;
- flex-direction: column;
- gap: 10px;
- margin-bottom: 20px;
- .import-option {
- border: 1px solid #efe6d8;
- border-radius: 16px;
- padding: 14px;
- display: flex;
- gap: 12px;
- cursor: pointer;
- transition: all 0.22s ease;
- background: #fffdfa;
- &:hover {
- border-color: rgba(247, 139, 34, 0.4);
- background: #fffdfa;
- }
- &.active {
- border-color: #ff9300;
- background: #fff5e5;
- .option-icon {
- border-color: #f78b22;
- &::after {
- content: '';
- position: absolute;
- inset: 3px;
- border-radius: 50%;
- background: #f78b22;
- }
- }
- }
- .option-icon {
- width: 18px;
- height: 18px;
- border-radius: 50%;
- border: 1.5px solid #d6c0a1;
- margin-top: 2px;
- position: relative;
- flex-shrink: 0;
- }
- .option-text {
- flex: 1;
- .option-title {
- font-size: 14px;
- font-weight: 600;
- color: #111827;
- margin-bottom: 3px;
- }
- .option-desc {
- font-size: 12px;
- color: #6b7280;
- line-height: 1.4;
- }
- }
- }
- }
- .modal-buttons {
- display: flex;
- gap: 10px;
- justify-content: flex-end;
- .cancel-btn,
- .confirm-btn {
- min-width: 84px;
- height: 34px;
- padding: 0 14px;
- border-radius: 10px;
- font-size: 12px;
- font-weight: 600;
- cursor: pointer;
- transition: all 0.2s ease;
- }
- .cancel-btn {
- background: #fff;
- border: 1px solid #e5e7eb;
- color: #6b7280;
- }
- .confirm-btn {
- background: linear-gradient(180deg, #f89a34 0%, #f07815 100%);
- border: 0;
- color: #fff;
- box-shadow: 0 10px 20px rgba(240, 120, 21, 0.18);
- }
- }
- }
- .line_box {
- height: calc(100% - 75px);
- overflow: hidden;
- }
- .upload-box {
- padding: 20px;
- .upload-tabs {
- display: flex;
- margin-bottom: 20px;
- border-bottom: 1px solid rgb(229, 231, 235);
- .upload-tab {
- border-top-width: initial;
- border-right-width: initial;
- border-left-width: initial;
- border-top-color: initial;
- border-right-color: initial;
- border-left-color: initial;
- background-color: transparent;
- cursor: pointer;
- font-size: 14px;
- color: rgb(107, 114, 128);
- margin-bottom: -1px;
- padding: 10px 16px;
- border-style: none none solid;
- border-image: initial;
- border-bottom: 2px solid transparent;
- &.active {
- color: rgb(247, 139, 34);
- border-bottom-color: rgb(247, 139, 34);
- font-weight: 600;
- }
- }
- }
- .uploadFilePanel {
- .form-group {
- display: flex;
- flex-direction: column;
- gap: 12px;
- .file-upload-area {
- text-align: center;
- cursor: pointer;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- min-height: 190px;
- border-width: 2px;
- border-style: dashed;
- border-color: rgb(229, 231, 235);
- border-image: initial;
- border-radius: 12px;
- background: rgb(250, 251, 252);
- padding: 40px 20px;
- transition: 0.2s;
- gap: 10px;
- svg {
- color: rgb(156, 163, 175);
- margin-bottom: 12px;
- }
- .upload-text {
- font-size: 14px;
- font-weight: 500;
- color: rgb(55, 65, 81);
- margin-bottom: 6px;
- }
- .upload-hint {
- font-size: 12px;
- color: rgb(107, 114, 128);
- }
- &:hover {
- border-color: rgb(247, 139, 34);
- background: rgb(255, 248, 240);
- }
- }
- .file-name-display {
- margin-top: 12px;
- font-size: 13px;
- color: rgb(75, 85, 99);
- background-color: rgb(249, 250, 251);
- border-radius: 6px;
- padding: 8px 12px;
- border-width: 1px;
- border-style: solid;
- border-color: rgb(229, 231, 235);
- border-image: initial;
- display: flex;
- justify-content: space-between;
- span {
- flex: 1;
- }
- .upload-task-action {
- border: 0;
- background: transparent;
- color: #9a8a77;
- font-size: 12px;
- font-weight: 600;
- cursor: pointer;
- }
- }
- }
- }
- .pasteCodePanel {
- .form-group {
- display: flex;
- flex-direction: column;
- gap: 10px;
- .code-textarea {
- width: 100%;
- min-height: 190px;
- font-family: monospace;
- font-size: 13px;
- resize: vertical;
- border-width: 1px;
- border-style: solid;
- border-color: rgb(209, 213, 219);
- border-image: initial;
- border-radius: 8px;
- padding: 12px;
- transition: border-color 0.2s;
- box-sizing: border-box;
- resize: none;
- &:focus {
- outline: none;
- border-color: rgb(247, 139, 34);
- }
- }
- }
- }
- .webpage-link-button {
- text-align: center;
- padding: 10px 24px;
- border: none;
- border-radius: 4px;
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- transition: all 0.3s;
- background-color: #FF9300;
- color: white;
- margin: 0 auto;
- display: block;
- margin-top: 15px;
- &:hover:not(:disabled) {
- background-color: #e68a00;
- }
- &:disabled {
- opacity: 0.5;
- cursor: not-allowed;
- }
- &.loading {
- cursor: not-allowed;
- opacity: 0.8;
- }
- }
- .loading-state {
- opacity: 0.7;
- cursor: not-allowed;
- pointer-events: none;
- .submenu-icon {
- animation: pulse 1s infinite;
- }
- .spin {
- animation: spin 1s linear infinite;
- }
- }
- }
- </style>
|