| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074 |
- <template>
- <div class="canvas-editor-wrapper">
- <div class="canvas-container">
- <el-container style="height: 100%">
- <el-header>
- <div class="menu" editor-component="menu">
- <div class="menu-item">
- <div class="menu-item__save" title="保存">
- <i class="el-icon-s-claim" style="color: #646464"></i>
- </div>
- <div class="menu-item__undo">
- <i></i>
- </div>
- <div class="menu-item__redo">
- <i></i>
- </div>
- <div class="menu-item__painter" title="格式刷(双击可连续使用)">
- <i></i>
- </div>
- <div class="menu-item__format" title="清除格式">
- <i></i>
- </div>
- </div>
- <div class="menu-divider"></div>
- <div class="menu-item">
- <div class="menu-item__font">
- <span class="select" title="字体">Arial</span>
- <div class="options">
- <ul>
- <li data-family="Arial" style="font-family: 'Arial'">
- Arial
- </li>
- <li
- data-family="Microsoft YaHei"
- style="font-family: 'Microsoft YaHei'"
- >
- 微软雅黑
- </li>
- <li data-family="宋体" style="font-family: '宋体'">宋体</li>
- <li data-family="黑体" style="font-family: '黑体'">黑体</li>
- <li data-family="仿宋" style="font-family: '仿宋'">仿宋</li>
- <li data-family="楷体" style="font-family: '楷体'">楷体</li>
- <li data-family="等线" style="font-family: '等线'">等线</li>
- <!-- <li data-family="华文琥珀" style="font-family:'华文琥珀';">华文琥珀</li>
- <li data-family="华文楷体" style="font-family:'华文楷体';">华文楷体</li>
- <li data-family="华文隶书" style="font-family:'华文隶书';">华文隶书</li>
- <li data-family="华文新魏" style="font-family:'华文新魏';">华文新魏</li>
- <li data-family="华文行楷" style="font-family:'华文行楷';">华文行楷</li>
- <li data-family="华文中宋" style="font-family:'华文中宋';">华文中宋</li>
- <li data-family="华文彩云" style="font-family:'华文彩云';">华文彩云</li> -->
- <li data-family="Segoe UI" style="font-family: 'Segoe UI'">
- Segoe UI
- </li>
- <li data-family="Ink Free" style="font-family: 'Ink Free'">
- Ink Free
- </li>
- <li data-family="Fantasy" style="font-family: 'Fantasy'">
- Fantasy
- </li>
- </ul>
- </div>
- </div>
- <div class="menu-item__size">
- <span class="select" title="字体">小四</span>
- <div class="options">
- <ul>
- <li data-size="56">初号</li>
- <li data-size="48">小初</li>
- <li data-size="34">一号</li>
- <li data-size="32">小一</li>
- <li data-size="29">二号</li>
- <li data-size="24">小二</li>
- <li data-size="21">三号</li>
- <li data-size="20">小三</li>
- <li data-size="18">四号</li>
- <li data-size="16">小四</li>
- <li data-size="14">五号</li>
- <li data-size="12">小五</li>
- <li data-size="10">六号</li>
- <li data-size="8">小六</li>
- <li data-size="7">七号</li>
- <li data-size="6">八号</li>
- </ul>
- </div>
- </div>
- <div class="menu-item__size-add">
- <i></i>
- </div>
- <div class="menu-item__size-minus">
- <i></i>
- </div>
- <div class="menu-item__bold">
- <i></i>
- </div>
- <div class="menu-item__italic">
- <i></i>
- </div>
- <div class="menu-item__underline">
- <i></i>
- </div>
- <div class="menu-item__strikeout" title="删除线(Ctrl+Shift+X)">
- <i></i>
- </div>
- <div class="menu-item__superscript">
- <i></i>
- </div>
- <div class="menu-item__subscript">
- <i></i>
- </div>
- <div class="menu-item__color" title="字体颜色">
- <i></i>
- <div class="options">
- <div class="color-section">
- <div class="section-title">主题颜色</div>
- <ul>
- <!-- 第一行 -->
- <li
- data-color="#000000"
- title="黑色"
- style="background-color: #000000"
- ></li>
- <li
- data-color="#2B579A"
- title="深蓝色"
- style="background-color: #2b579a"
- ></li>
- <li
- data-color="#4472C4"
- title="蓝色"
- style="background-color: #4472c4"
- ></li>
- <li
- data-color="#70AD47"
- title="绿色"
- style="background-color: #70ad47"
- ></li>
- <li
- data-color="#FFC000"
- title="黄色"
- style="background-color: #ffc000"
- ></li>
- <li
- data-color="#ED7D31"
- title="橙色"
- style="background-color: #ed7d31"
- ></li>
- <li
- data-color="#C00000"
- title="红色"
- style="background-color: #c00000"
- ></li>
- <li
- data-color="#7030A0"
- title="紫色"
- style="background-color: #7030a0"
- ></li>
- <li
- data-color="#FFFFFF"
- title="白色"
- style="background-color: #ffffff"
- ></li>
- <!-- 灰度行 -->
- <li
- data-color="#7F7F7F"
- title="深灰色"
- style="background-color: #7f7f7f"
- ></li>
- <li
- data-color="#A5A5A5"
- title="灰色"
- style="background-color: #a5a5a5"
- ></li>
- <li
- data-color="#D0D0D0"
- title="浅灰色"
- style="background-color: #d0d0d0"
- ></li>
- <!-- 蓝色系列 -->
- <li
- data-color="#D6E3F3"
- title="浅蓝色-1"
- style="background-color: #d6e3f3"
- ></li>
- <li
- data-color="#B4C7E7"
- title="浅蓝色-2"
- style="background-color: #b4c7e7"
- ></li>
- <li
- data-color="#8EAADB"
- title="浅蓝色-3"
- style="background-color: #8eaadb"
- ></li>
- <li
- data-color="#2E75B6"
- title="深蓝色-1"
- style="background-color: #2e75b6"
- ></li>
- <li
- data-color="#1F4E79"
- title="深蓝色-2"
- style="background-color: #1f4e79"
- ></li>
- </ul>
- </div>
- <div class="color-section">
- <div class="section-title">标准色</div>
- <ul>
- <li
- data-color="#FF0000"
- title="红色"
- style="background-color: #ff0000"
- ></li>
- <li
- data-color="#FF6600"
- title="橙色"
- style="background-color: #ff6600"
- ></li>
- <li
- data-color="#FFFF00"
- title="黄色"
- style="background-color: #ffff00"
- ></li>
- <li
- data-color="#00FF00"
- title="绿色"
- style="background-color: #00ff00"
- ></li>
- <li
- data-color="#00FFFF"
- title="青色"
- style="background-color: #00ffff"
- ></li>
- <li
- data-color="#0000FF"
- title="蓝色"
- style="background-color: #0000ff"
- ></li>
- <li
- data-color="#FF00FF"
- title="紫色"
- style="background-color: #ff00ff"
- ></li>
- </ul>
- </div>
- </div>
- </div>
- <div class="menu-item__highlight" title="高亮">
- <i></i>
- <div class="options">
- <ul>
- <!-- 第一行 -->
- <li
- data-color="transparent"
- title="无"
- style="background-color: transparent"
- >
- ✓
- </li>
- <li
- data-color="#ffff00"
- title="黄色"
- style="background-color: #ffff00"
- ></li>
- <li
- data-color="#00ff00"
- title="绿色"
- style="background-color: #00ff00"
- ></li>
- <li
- data-color="#00ffff"
- title="青色"
- style="background-color: #00ffff"
- ></li>
- <li
- data-color="#ff00ff"
- title="粉色"
- style="background-color: #ff00ff"
- ></li>
- <li
- data-color="#0000ff"
- title="蓝色"
- style="background-color: #0000ff"
- ></li>
- <!-- 第二行 -->
- <li
- data-color="#800000"
- title="深红色"
- style="background-color: #800000"
- ></li>
- <li
- data-color="#ff0000"
- title="红色"
- style="background-color: #ff0000"
- ></li>
- <li
- data-color="#808000"
- title="橄榄色"
- style="background-color: #808000"
- ></li>
- <li
- data-color="#008000"
- title="深绿色"
- style="background-color: #008000"
- ></li>
- <li
- data-color="#008080"
- title="青灰色"
- style="background-color: #008080"
- ></li>
- <li
- data-color="#000080"
- title="深蓝色"
- style="background-color: #000080"
- ></li>
- <!-- 第三行 -->
- <li
- data-color="#000000"
- title="黑色"
- style="background-color: #000000"
- ></li>
- <li
- data-color="#808080"
- title="深灰色"
- style="background-color: #808080"
- ></li>
- <li
- data-color="#c0c0c0"
- title="灰色"
- style="background-color: #c0c0c0"
- ></li>
- </ul>
- </div>
- </div>
- </div>
- <div class="menu-divider"></div>
- <div class="menu-item">
- <div class="menu-item__title">
- <i></i>
- <span class="select" title="切换标题">正文</span>
- <div class="options">
- <ul>
- <li style="font-size: 16px">正文</li>
- <li data-level="first" style="font-size: 26px">标题1</li>
- <li data-level="second" style="font-size: 24px">标题2</li>
- <li data-level="third" style="font-size: 22px">标题3</li>
- <li data-level="fourth" style="font-size: 20px">标题4</li>
- <li data-level="fifth" style="font-size: 18px">标题5</li>
- <li data-level="sixth" style="font-size: 16px">标题6</li>
- </ul>
- </div>
- </div>
- <div class="menu-item__left">
- <i></i>
- </div>
- <div class="menu-item__center">
- <i></i>
- </div>
- <div class="menu-item__right">
- <i></i>
- </div>
- <div class="menu-item__alignment">
- <i></i>
- </div>
- <div class="menu-item__row-margin">
- <i title="行间距"></i>
- <div class="options">
- <ul>
- <li data-rowmargin="1">1</li>
- <li data-rowmargin="1.25">1.25</li>
- <li data-rowmargin="1.5">1.5</li>
- <li data-rowmargin="1.75">1.75</li>
- <li data-rowmargin="2">2</li>
- <li data-rowmargin="2.5">2.5</li>
- <li data-rowmargin="3">3</li>
- </ul>
- </div>
- </div>
- <div class="menu-item__list">
- <i></i>
- <div class="options">
- <ul>
- <li>
- <label>取消列表</label>
- </li>
- <li data-list-type="ol" data-list-style="decimal">
- <label>有序列表:</label>
- <ol>
- <li>________</li>
- </ol>
- </li>
- <li data-list-type="ul" data-list-style="disc">
- <label>实心圆点列表:</label>
- <ul style="list-style-type: disc">
- <li>________</li>
- </ul>
- </li>
- <li data-list-type="ul" data-list-style="circle">
- <label>空心圆点列表:</label>
- <ul style="list-style-type: circle">
- <li>________</li>
- </ul>
- </li>
- <li data-list-type="ul" data-list-style="square">
- <label>空心方块列表:</label>
- <ul style="list-style-type: square">
- <li>________</li>
- </ul>
- </li>
- </ul>
- </div>
- </div>
- </div>
- <div class="menu-divider"></div>
- <div class="menu-item">
- <div class="menu-item__table">
- <i title="表格"></i>
- </div>
- <div class="menu-item__table__collapse">
- <div class="table-close">×</div>
- <div class="table-title">
- <span class="table-select">插入</span>
- <span>表格</span>
- </div>
- <div class="table-panel"></div>
- </div>
- <div class="menu-item__image">
- <i title="图片"></i>
- <input
- type="file"
- id="image"
- accept=".png, .jpg, .jpeg, .svg, .gif"
- />
- </div>
- <div class="menu-item__hyperlink">
- <i title="超链接"></i>
- </div>
- <div class="menu-item__separator">
- <i title="分割线"></i>
- <div class="options">
- <ul>
- <li data-separator="0,0">
- <i></i>
- </li>
- <li data-separator="1,1">
- <i></i>
- </li>
- <li data-separator="3,1">
- <i></i>
- </li>
- <li data-separator="4,4">
- <i></i>
- </li>
- <li data-separator="7,3,3,3">
- <i></i>
- </li>
- <li data-separator="6,2,2,2,2,2">
- <i></i>
- </li>
- </ul>
- </div>
- </div>
- <!-- <div class="menu-item__watermark">
- <i title="水印(添加、删除)"></i>
- <div class="options">
- <ul>
- <li data-menu="add">添加水印</li>
- <li data-menu="delete">删除水印</li>
- </ul>
- </div>
- </div>
- <div class="menu-item__codeblock" title="代码块">
- <i></i>
- </div>
- <div class="menu-item__page-break" title="分页符">
- <i></i>
- </div>
- <div class="menu-item__control">
- <i title="控件"></i>
- <div class="options">
- <ul>
- <li data-control='text'>文本</li>
- <li data-control="select">列举</li>
- <li data-control="checkbox">复选框</li>
- </ul>
- </div>
- </div>
- <div class="menu-item__checkbox" title="复选框">
- <i></i>
- </div>
- <div class="menu-item__latex" title="LateX">
- <i></i>
- </div>
- <div class="menu-item__date">
- <i title="日期"></i>
- <div class="options">
- <ul>
- <li data-format="yyyy-MM-dd"></li>
- <li data-format="yyyy-MM-dd hh:mm:ss"></li>
- </ul>
- </div>
- </div>
- <div class="menu-item__block" title="内容块">
- <i></i>
- </div> -->
- </div>
- <div class="menu-divider"></div>
- <div class="menu-item">
- <div class="menu-item__search" data-menu="search">
- <i></i>
- </div>
- <div class="menu-item__search__collapse" data-menu="search">
- <div class="menu-item__search__collapse__search">
- <input type="text" />
- <label class="search-result"></label>
- <div class="arrow-left">
- <i></i>
- </div>
- <div class="arrow-right">
- <i></i>
- </div>
- <span>×</span>
- </div>
- <div class="menu-item__search__collapse__replace">
- <input type="text" />
- <button>替换</button>
- </div>
- </div>
- <!-- <div class="menu-item__print" data-menu="print">
- <i></i>
- </div> -->
- </div>
- </div>
- </el-header>
- <el-main class="content">
- <!-- <div class="catalog" editor-component="catalog">
- <div class="catalog__header">
- <span>目录</span>
- <div class="catalog__header__close">
- <i></i>
- </div>
- </div>
- <div class="catalog__main"></div>
- </div> -->
- <div
- id="canvasEditor"
- class="canvas-editor"
- editor-component="main"
- ></div>
- <!-- <div class="comment" editor-component="comment"></div> -->
- </el-main>
- <el-footer style="height: 30px">
- <div class="footer" editor-component="footer">
- <div>
- <!-- <div class="catalog-mode" title="目录">
- <i></i>
- </div> -->
- <div class="page-mode">
- <i title="页面模式(分页、连页)"></i>
- <div class="options">
- <ul>
- <li data-page-mode="paging">分页</li>
- <li data-page-mode="continuity" class="active">连页</li>
- </ul>
- </div>
- </div>
- <span>可见页码:<span class="page-no-list">1</span></span>
- <span
- >页面:<span class="page-no">1</span>/<span class="page-size"
- >1</span
- ></span
- >
- <span>字数:<span class="word-count">0</span></span>
- </div>
- <div class="editor-mode" title="编辑模式(编辑、清洁、只读、表单)">
- 编辑模式
- </div>
- <div>
- <div class="page-scale-minus" title="缩小(Ctrl+-)">
- <i></i>
- </div>
- <span
- class="page-scale-percentage"
- title="显示比例(点击可复原Ctrl+0)"
- >100%</span
- >
- <div class="page-scale-add" title="放大(Ctrl+=)">
- <i></i>
- </div>
- <div class="paper-size">
- <i title="纸张类型"></i>
- <div class="options">
- <ul>
- <li data-paper-size="813*1123" class="active">A4</li>
- <li data-paper-size="1593*2251">A2</li>
- <li data-paper-size="1125*1593">A3</li>
- <li data-paper-size="565*796">A5</li>
- <li data-paper-size="412*488">5号信封</li>
- <li data-paper-size="450*866">6号信封</li>
- <li data-paper-size="609*862">7号信封</li>
- <li data-paper-size="862*1221">9号信封</li>
- <li data-paper-size="813*1266">法律用纸</li>
- <li data-paper-size="813*1054">信纸</li>
- </ul>
- </div>
- </div>
- <div class="paper-direction">
- <i title="纸张方向"></i>
- <div class="options">
- <ul>
- <li data-paper-direction="vertical" class="active">纵向</li>
- <li data-paper-direction="horizontal">横向</li>
- </ul>
- </div>
- </div>
- <div class="paper-margin" title="页边距">
- <i></i>
- </div>
- <div class="fullscreen" title="全屏显示">
- <i></i>
- </div>
- </div>
- </div>
- </el-footer>
- </el-container>
- </div>
- </div>
- </template>
- <script>
- import Editor from "@hufe921/canvas-editor";
- import { Dialog } from "./components/dialog/Dialog";
- import { Signature } from "./components/signature/Signature";
- import { IEditorOption, ITableOption, IHeader, IFooter } from "./option";
- import {
- BlockType,
- Command,
- ControlType,
- EditorMode,
- EditorZone,
- ElementType,
- IBlock,
- ICatalogItem,
- IElement,
- KeyMap,
- ListStyle,
- ListType,
- PageMode,
- PaperDirection,
- RowFlex,
- TitleLevel,
- splitText,
- } from "@hufe921/canvas-editor";
- export default {
- name: "CanvasEditor", // 确保设置了组件名称
- data() {
- return {
- editorRef: null, // 将editor实例存储在data中
- isApple:
- typeof navigator !== "undefined" &&
- /Mac OS X/.test(navigator.userAgent),
- // 编辑模式
- modeList: [
- {
- mode: EditorMode.READONLY,
- name: "只读模式",
- },
- {
- mode: EditorMode.EDIT,
- name: "编辑模式",
- },
- ],
- header: [], // 页眉配置
- main: [], // 主要编辑内容
- footer: [], // 页脚信息
- options: {
- width: 813, // A4 纸张宽度
- height: 1123, // A4 纸张高度
- defaultSize: 14,//Number(this.editorOptions.size),
- defaultFont: this.editorOptions.font,
- // defaultSize: 14,//Number(this.editorOptions.size),
- defaultBold: this.editorOptions.bold,
- defaultAlign: this.editorOptions.align,
- pageMode: 'continuity',
- }, // IEditorOption,
- // 批注 TODO
- commentList: [],
- };
- },
- props: {
- // 编辑模式
- editMode: {
- type: String,
- },
- // html数据
- htmlData: {
- type: String,
- },
- // 后端传过来的保存html的JSON数据(用于回显)
- docJson: {
- type: String,
- },
- editorOptions: {
- type: Object,
- default: () => ({
- defaultFont: "Arial",
- //defaultSize: 14,
- defaultBold: false,
- defaultAlign: "left",
- }),
- },
- },
- watch: {
- // 监听父组件传过来的编辑模式,设置模式
- editMode: {
- handler(val) {
- if (this.editorRef) {
- this.editorRef.command.executeMode(val);
- // 设置模式
- const modeElement = document.querySelector(".editor-mode");
- modeElement.innerText =
- this.modeList
- .filter((item) => item.mode == val)
- .map((data) => data.name) || "";
- // 设置菜单栏权限视觉反馈
- const isReadonly = val === EditorMode.READONLY;
- const enableMenuList = ["search", "print"];
- document.querySelectorAll(".menu-item>div").forEach((dom) => {
- const menu = dom.dataset.menu;
- isReadonly && (!menu || !enableMenuList.includes(menu))
- ? dom.classList.add("disable")
- : dom.classList.remove("disable");
- });
- }
- },
- deep: true,
- },
- },
- methods: {
- debounce(func, delay) {
- let timer;
- return function (...args) {
- if (timer) {
- window.clearTimeout(timer);
- }
- timer = window.setTimeout(() => {
- func.apply(this, args);
- }, delay);
- };
- },
- applyBoldToAll() {
- if (!this.editorRef) return;
-
- try {
- this.editorRef.command.executeSelectAll();
- this.editorRef.command.executeBold();
- this.editorRef.command.executeSelectAll();
- this.editorRef.command.executeSelect(0, 0);
- } catch (error) {
- console.error('Apply bold failed:', error);
- }
- },
- // 修改为异步的粘贴处理方法
- async handlePaste(e) {
- try {
- if (!this.editorRef) return;
-
- // 阻止默认粘贴行为
- e.preventDefault();
-
- let text = '';
- let htmlContent = '';
-
- // 优先尝试使用现代的 Async Clipboard API
- if (navigator.clipboard && navigator.clipboard.readText) {
- text = await navigator.clipboard.readText();
- // 尝试获取HTML内容
- if (navigator.clipboard.read) {
- try {
- const clipboardItems = await navigator.clipboard.read();
- for (const clipboardItem of clipboardItems) {
- for (const type of clipboardItem.types) {
- if (type === 'text/html') {
- const htmlBlob = await clipboardItem.getType(type);
- htmlContent = await htmlBlob.text();
- break;
- }
- }
- if (htmlContent) break;
- }
- } catch (htmlError) {
- console.log('HTML clipboard access failed, using text only:', htmlError);
- }
- }
- }
- // 作为备选,尝试从粘贴事件对象中获取
- else if (e.clipboardData && e.clipboardData.getData) {
- text = e.clipboardData.getData('text/plain');
- htmlContent = e.clipboardData.getData('text/html');
- }
- // 最后降级到已不推荐的 window.clipboardData (主要针对旧版IE)
- else if (window.clipboardData && window.clipboardData.getData) {
- text = window.clipboardData.getData('Text');
- htmlContent = window.clipboardData.getData('Text');
- }
-
- // 如果成功获取到内容,则执行插入操作
- if (text || htmlContent) {
- // 如果有HTML内容,先处理HTML以清理超链接样式
- if (htmlContent && htmlContent.trim()) {
- console.log('Processing HTML content from clipboard');
- const processedHtml = this.processPastedHtml(htmlContent);
- // 使用处理后的HTML内容
- this.editorRef.command.executeSetHTML({ main: processedHtml });
- } else if (text && text.trim()) {
- console.log('Processing plain text from clipboard');
- // 使用纯文本
- this.editorRef.command.executeInsertText(text);
- } else {
- console.warn('Both HTML and text content are empty or invalid.');
- }
- } else {
- console.warn('Failed to retrieve any content from clipboard.');
- }
- } catch (error) {
- console.error('Custom paste handler failed:', error);
- // 错误处理见下一步
- }
- },
-
- // 处理粘贴的HTML内容,清理超链接样式
- processPastedHtml(htmlContent) {
- try {
- console.log('Processing pasted HTML:', htmlContent);
-
- // 创建临时DOM元素来解析HTML,保持结构完整
- const tempDiv = document.createElement('div');
- tempDiv.innerHTML = htmlContent;
-
- // 查找并处理所有超链接,但保持其他样式
- const links = tempDiv.querySelectorAll('a');
- links.forEach(link => {
- // 创建span元素替换超链接
- const span = document.createElement('span');
-
- // 复制超链接的文本内容
- span.textContent = link.textContent;
-
- // 复制父元素的样式(保持表头底色等)
- const parentElement = link.parentElement;
- if (parentElement && parentElement.style) {
- const parentStyles = window.getComputedStyle(parentElement);
- // 复制重要的样式属性,但排除链接相关样式
- const stylesToCopy = [
- 'background-color', 'background', 'font-family', 'font-size',
- 'font-weight', 'font-style', 'text-align', 'padding', 'margin',
- 'border', 'border-color', 'border-width', 'border-style'
- ];
-
- stylesToCopy.forEach(styleProp => {
- const value = parentStyles.getPropertyValue(styleProp);
- if (value && value !== 'initial' && value !== 'inherit' && value !== 'transparent') {
- span.style.setProperty(styleProp, value);
- }
- });
- }
-
- // 设置文本样式为普通文本
- span.style.color = 'inherit';
- span.style.textDecoration = 'none';
- span.style.cursor = 'default';
-
- // 替换超链接元素
- link.parentNode.replaceChild(span, link);
- });
-
- // 特别处理表格,确保表头样式正确
- const tables = tempDiv.querySelectorAll('table');
- tables.forEach(table => {
- // 处理表头行
- const headerRows = table.querySelectorAll('thead tr, tr:first-child');
- headerRows.forEach(headerRow => {
- // 确保表头有背景色
- if (!headerRow.style.backgroundColor) {
- headerRow.style.backgroundColor = '#f5f5f5'; // 默认表头背景色
- }
-
- // 处理表头单元格
- const headerCells = headerRow.querySelectorAll('th, td');
- headerCells.forEach(cell => {
- // 确保表头单元格有背景色
- if (!cell.style.backgroundColor) {
- cell.style.backgroundColor = '#f5f5f5';
- }
-
- // 处理单元格内的超链接
- const cellLinks = cell.querySelectorAll('a');
- cellLinks.forEach(cellLink => {
- const span = document.createElement('span');
- span.textContent = cellLink.textContent;
-
- // 保持表头样式
- span.style.color = 'inherit';
- span.style.textDecoration = 'none';
- span.style.cursor = 'default';
- span.style.fontWeight = 'bold'; // 表头通常加粗
-
- cellLink.parentNode.replaceChild(span, cellLink);
- });
- });
- });
-
- // 处理表格单元格中的超链接
- const cellLinks = table.querySelectorAll('td a, tbody a');
- cellLinks.forEach(cellLink => {
- const span = document.createElement('span');
- span.textContent = cellLink.textContent;
-
- // 保持单元格的默认样式
- span.style.color = 'inherit';
- span.style.textDecoration = 'none';
- span.style.cursor = 'default';
-
- cellLink.parentNode.replaceChild(span, cellLink);
- });
- });
-
- const result = tempDiv.innerHTML;
- console.log('Processed HTML result:', result);
-
- return result;
- } catch (error) {
- console.error('Error processing pasted HTML:', error);
- // 如果处理失败,尝试简单的文本提取
- try {
- const tempDiv = document.createElement('div');
- tempDiv.innerHTML = htmlContent;
- return tempDiv.textContent || tempDiv.innerText || htmlContent;
- } catch (fallbackError) {
- console.error('Fallback processing also failed:', fallbackError);
- return htmlContent;
- }
- }
- }
- },
- beforeDestroy() {
- // 清理事件监听器
- const editorElement = document.querySelector(".canvas-editor");
- if (editorElement && this.eventHandlers) {
- editorElement.removeEventListener('paste', this.eventHandlers.paste, true);
- editorElement.removeEventListener('keydown', this.eventHandlers.keydown, true);
- editorElement.removeEventListener('contextmenu', this.eventHandlers.contextmenu, true);
- }
- if (this.eventHandlers && this.eventHandlers.documentPaste) {
- document.removeEventListener('paste', this.eventHandlers.documentPaste, true);
- }
- if (this.globalPasteHandler) {
- document.removeEventListener('paste', this.globalPasteHandler, true);
- window.removeEventListener('paste', this.globalPasteHandler, true);
- }
- if (this.globalClickHandler) {
- document.removeEventListener('click', this.globalClickHandler, true);
- }
- },
- mounted() {
- console.log(this.editorOptions);
- const isApple =
- typeof navigator !== "undefined" && /Mac OS X/.test(navigator.userAgent);
- const instance = new Editor(
- document.querySelector(".canvas-editor"),
- {
- header: this.header,
- main: this.main,
- footer: this.footer,
- defaultFont: this.editorOptions.font,
- // defaultSize: 14,//Number(this.editorOptions.size),
- defaultBold: this.editorOptions.bold,
- defaultAlign: this.editorOptions.align,
- },
- this.options
- );
- this.editorRef = instance;
- // cypress使用
- Reflect.set(window, "editor", instance);
-
- // 重写编辑器的粘贴命令,确保所有粘贴操作都经过我们的处理
- const originalExecuteInsertText = instance.command.executeInsertText;
- const originalExecuteSetHTML = instance.command.executeSetHTML;
-
- // 重写 executeInsertText 方法
- instance.command.executeInsertText = (text) => {
- console.log('Intercepted executeInsertText:', text);
- // 直接调用原始方法,因为纯文本不需要特殊处理
- return originalExecuteInsertText.call(instance.command, text);
- };
-
- // 重写 executeSetHTML 方法
- instance.command.executeSetHTML = (htmlData) => {
- console.log('Intercepted executeSetHTML:', htmlData);
- if (htmlData && htmlData.main) {
- // 处理HTML内容
- const processedHtml = this.processPastedHtml(htmlData.main);
- htmlData.main = processedHtml;
- }
- return originalExecuteSetHTML.call(instance.command, htmlData);
- };
-
- // 添加全局粘贴事件监听器,确保所有粘贴操作都经过处理
- const editorElement = document.querySelector(".canvas-editor");
- if (editorElement) {
- // 创建事件处理器引用
- this.eventHandlers = {
- paste: this.handlePaste.bind(this),
- keydown: (e) => {
- if ((e.ctrlKey || e.metaKey) && e.key === 'v') {
- e.preventDefault();
- this.handlePaste(e);
- }
- },
- contextmenu: (e) => {
- // 使用MutationObserver监听动态创建的右键菜单
- const observer = new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- // 查找所有菜单项
- const allMenuItems = node.querySelectorAll ?
- node.querySelectorAll('*') : [];
-
- // 也检查节点本身
- const allItems = [...allMenuItems, node];
-
- allItems.forEach(item => {
- // 为所有菜单项添加点击事件监听器
- if (item.textContent && (
- item.textContent.includes('粘贴') ||
- item.textContent.includes('剪切') ||
- item.textContent.includes('全选') ||
- item.textContent.includes('打印') ||
- item.textContent.includes('签名') ||
- item.textContent.includes('格式整理')
- )) {
- console.log('Found menu item:', item.textContent);
- item.addEventListener('click', (e) => {
- e.preventDefault();
- e.stopPropagation();
- console.log('Menu item clicked:', item.textContent);
-
- // 处理特定功能
- if (item.textContent.includes('粘贴')) {
- this.handlePaste(e);
- }
-
- // 点击菜单项后关闭菜单
- setTimeout(() => {
- this.closeAllContextMenus();
- }, 100);
- }, true);
- }
- });
- }
- });
- });
- });
-
- // 开始观察文档变化
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
-
- // 5秒后停止观察(避免内存泄漏)
- setTimeout(() => {
- observer.disconnect();
- }, 5000);
- },
- documentPaste: (e) => {
- // 检查是否在编辑器区域内
- const editorElement = document.querySelector(".canvas-editor");
- if (editorElement && editorElement.contains(e.target)) {
- e.preventDefault();
- this.handlePaste(e);
- }
- }
- };
-
- // 监听所有可能的粘贴事件
- editorElement.addEventListener('paste', this.eventHandlers.paste, true);
-
- // 监听键盘粘贴事件
- editorElement.addEventListener('keydown', this.eventHandlers.keydown, true);
-
- // 监听右键菜单粘贴(如果存在)
- editorElement.addEventListener('contextmenu', this.eventHandlers.contextmenu, true);
- }
-
- // 监听整个文档的粘贴事件作为备用
- if (this.eventHandlers) {
- document.addEventListener('paste', this.eventHandlers.documentPaste, true);
- }
-
- // 添加全局的粘贴拦截,确保所有粘贴操作都被拦截
- const globalPasteHandler = (e) => {
- const editorElement = document.querySelector(".canvas-editor");
- if (editorElement && editorElement.contains(e.target)) {
- console.log('Global paste intercepted');
- e.preventDefault();
- e.stopPropagation();
- this.handlePaste(e);
- }
- };
-
- // 监听所有可能的粘贴事件
- document.addEventListener('paste', globalPasteHandler, true);
- window.addEventListener('paste', globalPasteHandler, true);
-
- // 保存处理器引用以便清理
- this.globalPasteHandler = globalPasteHandler;
- // 添加全局点击事件监听器,用于关闭右键菜单
- this.globalClickHandler = (e) => {
- // 检查点击的元素是否是右键菜单
- const contextMenus = document.querySelectorAll('.ce-contextmenu, [class*="contextmenu"], [class*="context-menu"]');
- let isClickOnMenu = false;
- let clickedMenuItem = false;
-
- contextMenus.forEach(menu => {
- if (menu.contains(e.target)) {
- isClickOnMenu = true;
- // 检查是否点击的是菜单项(按钮)
- const menuItems = menu.querySelectorAll('li, div[role="menuitem"], button, a, span');
- menuItems.forEach(item => {
- if (item.contains(e.target) && item !== menu) {
- clickedMenuItem = true;
- }
- });
- }
- });
-
- // 如果点击的是菜单项或者不是菜单,则关闭所有右键菜单
- if (clickedMenuItem || !isClickOnMenu) {
- this.closeAllContextMenus();
- }
- };
-
- // 关闭所有右键菜单的方法
- this.closeAllContextMenus = () => {
- const contextMenus = document.querySelectorAll('.ce-contextmenu, [class*="contextmenu"], [class*="context-menu"]');
- contextMenus.forEach(menu => {
- if (menu.style.display !== 'none') {
- menu.style.display = 'none';
- menu.style.visibility = 'hidden';
- }
- });
-
- // 也尝试通过移除菜单元素来关闭
- const menuElements = document.querySelectorAll('.ce-contextmenu');
- menuElements.forEach(element => {
- if (element.parentNode) {
- element.parentNode.removeChild(element);
- }
- });
- };
-
- // 监听全局点击事件
- document.addEventListener('click', this.globalClickHandler, true);
- // 回显编辑器数据
- if (this.docJson !== null) {
- // 通过getValue来的数据回显页面(因为用html回显页面会丢掉font-family,官网git issue有解释)
- instance.command.executeSetValue({ main: JSON.parse(this.docJson) });
- } else {
- // 处理后端返回的html字符串
- // 先替换 \r\n 为 空格,以统一处理空格问题
- let step1 = this.htmlData.replace(/\r\n/g, " ");
- // 然后替换 \\\" 为 \" ,确保样式字符串内的引号正确
- let step2 = step1.replace(/\\\"/g, '"');
- // 接着替换 \\ 为 空字符,去掉其他不必要的转义
- let cleanedHtml = step2.replace(/\\+/g, "");
- // 设置Word模板数据
- instance.command.executeSetHTML({ main: cleanedHtml });
- }
- // 菜单弹窗销毁
- window.addEventListener(
- "click",
- (evt) => {
- const visibleDom = document.querySelector(".visible");
- if (!visibleDom || visibleDom.contains(evt.target)) return;
- visibleDom.classList.remove("visible");
- },
- {
- capture: true,
- }
- );
- /* if (this.editorOptions.align == "") {
- instance.command.executeRowFlex(RowFlex.CENTER);
- }
- if (this.editorOptions.bold == "不加粗") {
- console.log(instance);
- // 等待editor实例初始化完成
- this.$nextTick(() => {
- instance.command.executeBold(); // 切换加粗状态
- });
- } */
- /*
- 工具栏方法
- */
- // 1.保存(自定义)
- const saveDom = document.querySelector(".menu-item__save");
- saveDom.title = `保存(${this.isApple ? "⌘" : "Ctrl"}+S)`;
- saveDom.onclick = () => {
- const value = instance.command.getValue(this.options);
- const htmlVal = instance.command.getHTML();
- this.$emit("save", htmlVal); // 保存数据传给父组件
- };
- // 快捷键保存
- instance.listener.saved = (payload) => {
- console.log("elementList: ", payload);
- this.$emit("save", htmlVal); // 保存数据传给父组件
- };
- // 2. | 撤销 | 重做 | 格式刷 | 清除格式 |
- const undoDom = document.querySelector(".menu-item__undo");
- undoDom.title = `撤销(${isApple ? "⌘" : "Ctrl"}+Z)`;
- undoDom.onclick = function () {
- console.log("undo");
- instance.command.executeUndo();
- };
- const redoDom = document.querySelector(".menu-item__redo");
- redoDom.title = `重做(${isApple ? "⌘" : "Ctrl"}+Y)`;
- redoDom.onclick = function () {
- console.log("redo");
- instance.command.executeRedo();
- };
- const painterDom = document.querySelector(".menu-item__painter");
- painterDom.onclick = function () {
- console.log("painter");
- instance.command.executePainter({
- isDblclick: false,
- });
- };
- painterDom.ondblclick = function () {
- console.log("painter");
- instance.command.executePainter({
- isDblclick: true,
- });
- };
- document.querySelector(".menu-item__format").onclick = function () {
- console.log("format");
- instance.command.executeFormat();
- };
- // 3. | 字体 | 字体变大 | 字体变小 | 加粗 | 斜体 | 下划线 | 删除线 | 上标 | 下标 | 字体颜色 | 背景色 |
- const fontDom = document.querySelector(".menu-item__font");
- const fontSelectDom = fontDom.querySelector(".select");
- const fontOptionDom = fontDom.querySelector(".options");
- fontDom.onclick = function () {
- console.log("font");
- fontOptionDom.classList.toggle("visible");
- };
- fontOptionDom.onclick = function (evt) {
- const li = evt.target;
- instance.command.executeFont(li.dataset.family);
- };
- const sizeSetDom = document.querySelector(".menu-item__size");
- const sizeSelectDom = sizeSetDom.querySelector(".select");
- const sizeOptionDom = sizeSetDom.querySelector(".options");
- sizeSetDom.title = `设置字号`;
- sizeSetDom.onclick = function () {
- console.log("size");
- sizeOptionDom.classList.toggle("visible");
- };
- sizeOptionDom.onclick = function (evt) {
- const li = evt.target;
- instance.command.executeSize(Number(li.dataset.size));
- };
- const sizeAddDom = document.querySelector(".menu-item__size-add");
- sizeAddDom.title = `增大字号(${this.isApple ? "⌘" : "Ctrl"}+[)`;
- sizeAddDom.onclick = function () {
- console.log("size-add");
- instance.command.executeSizeAdd();
- };
- const sizeMinusDom = document.querySelector(".menu-item__size-minus");
- sizeMinusDom.title = `减小字号(${this.isApple ? "⌘" : "Ctrl"}+])`;
- sizeMinusDom.onclick = function () {
- console.log("size-minus");
- instance.command.executeSizeMinus();
- };
- const boldDom = document.querySelector(".menu-item__bold");
- boldDom.title = `加粗(${this.isApple ? "⌘" : "Ctrl"}+B)`;
- boldDom.onclick = function () {
- console.log("bold");
- instance.command.executeBold();
- };
- const italicDom = document.querySelector(".menu-item__italic");
- italicDom.title = `斜体(${this.isApple ? "⌘" : "Ctrl"}+I)`;
- italicDom.onclick = function () {
- console.log("italic");
- instance.command.executeItalic();
- };
- const underlineDom = document.querySelector(".menu-item__underline");
- underlineDom.title = `下划线(${this.isApple ? "⌘" : "Ctrl"}+U)`;
- underlineDom.onclick = function () {
- console.log("underline");
- instance.command.executeUnderline();
- };
- const strikeoutDom = document.querySelector(".menu-item__strikeout");
- strikeoutDom.onclick = function () {
- console.log("strikeout");
- instance.command.executeStrikeout();
- };
- const superscriptDom = document.querySelector(".menu-item__superscript");
- superscriptDom.title = `上标(${this.isApple ? "⌘" : "Ctrl"}+Shift+,)`;
- superscriptDom.onclick = function () {
- console.log("superscript");
- instance.command.executeSuperscript();
- };
- const subscriptDom = document.querySelector(".menu-item__subscript");
- subscriptDom.title = `下标(${this.isApple ? "⌘" : "Ctrl"}+Shift+.)`;
- subscriptDom.onclick = function () {
- console.log("subscript");
- instance.command.executeSubscript();
- };
- const colorDom = document.querySelector(".menu-item__color");
- const colorOptionDom = colorDom.querySelector(".options");
- colorDom.onclick = function () {
- console.log("color");
- colorOptionDom.classList.toggle("visible");
- };
- colorOptionDom.onclick = function (evt) {
- const li = evt.target;
- const color = li.dataset.color;
- if (color) {
- instance.command.executeColor(color);
- }
- };
- // 高亮功能代码
- const highlightDom = document.querySelector(".menu-item__highlight");
- const highlightOptionDom = highlightDom.querySelector(".options");
- highlightDom.onclick = function () {
- console.log("highlight");
- highlightOptionDom.classList.toggle("visible");
- };
- highlightOptionDom.onclick = function (evt) {
- const li = evt.target;
- const color = li.dataset.color;
- if (color) {
- // 如果选择"无",则清除高亮
- if (color === "transparent") {
- instance.command.executeHighlight(null);
- } else {
- instance.command.executeHighlight(color);
- }
- }
- };
- const titleDom = document.querySelector(".menu-item__title");
- const titleSelectDom = titleDom.querySelector(".select");
- const titleOptionDom = titleDom.querySelector(".options");
- titleOptionDom.querySelectorAll("li").forEach((li, index) => {
- li.title = `Ctrl+${this.isApple ? "Option" : "Alt"}+${index}`;
- });
- titleDom.onclick = function () {
- console.log("title");
- titleOptionDom.classList.toggle("visible");
- };
- titleOptionDom.onclick = function (evt) {
- const li = evt.target;
- const level = li.dataset.level;
- instance.command.executeTitle(level || null);
- };
- const leftDom = document.querySelector(".menu-item__left");
- leftDom.title = `左对齐(${this.isApple ? "⌘" : "Ctrl"}+L)`;
- leftDom.onclick = function () {
- console.log("left");
- instance.command.executeRowFlex(RowFlex.LEFT);
- };
- const centerDom = document.querySelector(".menu-item__center");
- centerDom.title = `居中对齐(${this.isApple ? "⌘" : "Ctrl"}+E)`;
- centerDom.onclick = function () {
- console.log("center");
- instance.command.executeRowFlex(RowFlex.CENTER);
- };
- const rightDom = document.querySelector(".menu-item__right");
- rightDom.title = `右对齐(${this.isApple ? "⌘" : "Ctrl"}+R)`;
- rightDom.onclick = function () {
- console.log("right");
- instance.command.executeRowFlex(RowFlex.RIGHT);
- };
- const alignmentDom = document.querySelector(".menu-item__alignment");
- alignmentDom.title = `两端对齐(${this.isApple ? "⌘" : "Ctrl"}+J)`;
- alignmentDom.onclick = function () {
- console.log("alignment");
- instance.command.executeRowFlex(RowFlex.ALIGNMENT);
- };
- const rowMarginDom = document.querySelector(".menu-item__row-margin");
- const rowOptionDom = rowMarginDom.querySelector(".options");
- rowMarginDom.onclick = function () {
- console.log("row-margin");
- rowOptionDom.classList.toggle("visible");
- };
- rowOptionDom.onclick = function (evt) {
- const li = evt.target;
- instance.command.executeRowMargin(Number(li.dataset.rowmargin));
- };
- const listDom = document.querySelector(".menu-item__list");
- listDom.title = `列表(${this.isApple ? "⌘" : "Ctrl"}+Shift+U)`;
- const listOptionDom = listDom.querySelector(".options");
- listDom.onclick = function () {
- console.log("list");
- listOptionDom.classList.toggle("visible");
- };
- listOptionDom.onclick = function (evt) {
- const li = evt.target;
- const listType = li.dataset.listType || null;
- const listStyle = li.dataset.listStyle;
- instance.command.executeList(listType, listStyle);
- };
- // 4. | 表格 | 图片 | 超链接 | 分割线 | 水印 | 代码块 | 分隔符 | 控件 | 复选框 | LaTeX | 日期选择器
- const tableDom = document.querySelector(".menu-item__table");
- const tablePanelContainer = document.querySelector(
- ".menu-item__table__collapse"
- );
- const tableClose = document.querySelector(".table-close");
- const tableTitle = document.querySelector(".table-select");
- const tablePanel = document.querySelector(".table-panel");
- // 绘制行列
- const tableCellList = [];
- for (let i = 0; i < 10; i++) {
- const tr = document.createElement("tr");
- tr.classList.add("table-row");
- const trCellList = [];
- for (let j = 0; j < 10; j++) {
- const td = document.createElement("td");
- td.classList.add("table-cel");
- tr.append(td);
- trCellList.push(td);
- }
- tablePanel.append(tr);
- tableCellList.push(trCellList);
- }
- let colIndex = 0;
- let rowIndex = 0;
- // 移除所有格选择
- function removeAllTableCellSelect() {
- tableCellList.forEach((tr) => {
- tr.forEach((td) => td.classList.remove("active"));
- });
- }
- // 设置标题内容
- function setTableTitle(payload) {
- tableTitle.innerText = payload;
- }
- // 恢复初始状态
- function recoveryTable() {
- // 还原选择样式、标题、选择行列
- removeAllTableCellSelect();
- setTableTitle("插入");
- colIndex = 0;
- rowIndex = 0;
- // 隐藏panel
- tablePanelContainer.style.display = "none";
- }
- tableDom.onclick = function () {
- console.log("table");
- tablePanelContainer.style.display = "block";
- };
- tablePanel.onmousemove = function (evt) {
- const celSize = 16;
- const rowMarginTop = 10;
- const celMarginRight = 6;
- const { offsetX, offsetY } = evt;
- // 移除所有选择
- removeAllTableCellSelect();
- colIndex = Math.ceil(offsetX / (celSize + celMarginRight)) || 1;
- rowIndex = Math.ceil(offsetY / (celSize + rowMarginTop)) || 1;
- // 改变选择样式
- tableCellList.forEach((tr, trIndex) => {
- tr.forEach((td, tdIndex) => {
- if (tdIndex < colIndex && trIndex < rowIndex) {
- td.classList.add("active");
- }
- });
- });
- // 改变表格标题
- setTableTitle(`${rowIndex}×${colIndex}`);
- };
- tableClose.onclick = function () {
- recoveryTable();
- };
- tablePanel.onclick = function () {
- // 应用选择
- instance.command.executeInsertTable(rowIndex, colIndex);
- recoveryTable();
- };
- const imageDom = document.querySelector(".menu-item__image");
- const imageFileDom = document.querySelector("#image");
- imageDom.onclick = function () {
- imageFileDom.click();
- };
- imageFileDom.onchange = function () {
- const file = imageFileDom.files[0];
- const fileReader = new FileReader();
- fileReader.readAsDataURL(file);
- fileReader.onload = function () {
- // 计算宽高
- const image = new Image();
- const value = String(fileReader.result);
- image.src = value;
- image.onload = function () {
- instance.command.executeImage({
- value,
- width: image.width,
- height: image.height,
- });
- imageFileDom.value = "";
- };
- };
- };
- const hyperlinkDom = document.querySelector(".menu-item__hyperlink");
- hyperlinkDom.onclick = function () {
- console.log("hyperlink");
- new Dialog({
- title: "超链接",
- data: [
- {
- type: "text",
- label: "文本",
- name: "name",
- required: true,
- placeholder: "请输入文本",
- value: instance.command.getRangeText(),
- },
- {
- type: "text",
- label: "链接",
- name: "url",
- required: true,
- placeholder: "请输入链接",
- },
- ],
- onConfirm: (payload) => {
- const name = payload.find((p) => p.name === "name")?.value;
- if (!name) return;
- const url = payload.find((p) => p.name === "url")?.value;
- if (!url) return;
- instance.command.executeHyperlink({
- type: ElementType.HYPERLINK,
- value: "",
- url,
- valueList: splitText(name).map((n) => ({
- value: n,
- size: 16,
- })),
- });
- },
- });
- };
- const separatorDom = document.querySelector(".menu-item__separator");
- const separatorOptionDom = separatorDom.querySelector(".options");
- separatorDom.onclick = function () {
- console.log("separator");
- separatorOptionDom.classList.toggle("visible");
- };
- separatorOptionDom.onmousedown = function (evt) {
- let payload = [];
- const li = evt.target;
- const separatorDash = li.dataset.separator?.split(",").map(Number);
- if (separatorDash) {
- const isSingleLine = separatorDash.every((d) => d === 0);
- if (!isSingleLine) {
- payload = separatorDash;
- }
- }
- instance.command.executeSeparator(payload);
- };
- // const pageBreakDom = document.querySelector(
- // '.menu-item__page-break'
- // )
- // pageBreakDom.onclick = function () {
- // console.log('pageBreak')
- // instance.command.executePageBreak()
- // }
- // const watermarkDom = document.querySelector(
- // '.menu-item__watermark'
- // )
- // const watermarkOptionDom =
- // watermarkDom.querySelector('.options')
- // watermarkDom.onclick = function () {
- // console.log('watermark')
- // watermarkOptionDom.classList.toggle('visible')
- // }
- // watermarkOptionDom.onmousedown = function (evt) {
- // const li = evt.target
- // const menu = li.dataset.menu
- // watermarkOptionDom.classList.toggle('visible')
- // if (menu === 'add') {
- // new Dialog({
- // title: '水印',
- // data: [
- // {
- // type: 'text',
- // label: '内容',
- // name: 'data',
- // required: true,
- // placeholder: '请输入内容'
- // },
- // {
- // type: 'color',
- // label: '颜色',
- // name: 'color',
- // required: true,
- // value: '#AEB5C0'
- // },
- // {
- // type: 'number',
- // label: '字体大小',
- // name: 'size',
- // required: true,
- // value: '120'
- // }
- // ],
- // onConfirm: payload => {
- // const nullableIndex = payload.findIndex(p => !p.value)
- // if (~nullableIndex) return
- // const watermark = payload.reduce((pre, cur) => {
- // pre[cur.name] = cur.value
- // return pre
- // }, {})
- // instance.command.executeAddWatermark({
- // data: watermark.data,
- // color: watermark.color,
- // size: Number(watermark.size)
- // })
- // }
- // })
- // } else {
- // instance.command.executeDeleteWatermark()
- // }
- // }
- // const codeblockDom = document.querySelector(
- // '.menu-item__codeblock'
- // )
- // codeblockDom.onclick = function () {
- // console.log('codeblock')
- // new Dialog({
- // title: '代码块',
- // data: [
- // {
- // type: 'textarea',
- // name: 'codeblock',
- // placeholder: '请输入代码',
- // width: 500,
- // height: 300
- // }
- // ],
- // onConfirm: payload => {
- // const codeblock = payload.find(p => p.name === 'codeblock')?.value
- // if (!codeblock) return
- // const tokenList = prism.tokenize(codeblock, prism.languages.javascript)
- // const formatTokenList = formatPrismToken(tokenList)
- // const elementList = []
- // for (let i = 0; i < formatTokenList.length; i++) {
- // const formatToken = formatTokenList[i]
- // const tokenStringList = splitText(formatToken.content)
- // for (let j = 0; j < tokenStringList.length; j++) {
- // const value = tokenStringList[j]
- // const element = {
- // value
- // }
- // if (formatToken.color) {
- // element.color = formatToken.color
- // }
- // if (formatToken.bold) {
- // element.bold = true
- // }
- // if (formatToken.italic) {
- // element.italic = true
- // }
- // elementList.push(element)
- // }
- // }
- // elementList.unshift({
- // value: '\n'
- // })
- // instance.command.executeInsertElementList(elementList)
- // }
- // })
- // }
- // const controlDom = document.querySelector(
- // '.menu-item__control'
- // )
- // const controlOptionDom = controlDom.querySelector('.options')
- // controlDom.onclick = function () {
- // console.log('control')
- // controlOptionDom.classList.toggle('visible')
- // }
- // controlOptionDom.onmousedown = function (evt) {
- // controlOptionDom.classList.toggle('visible')
- // const li = evt.target
- // const type = li.dataset.control
- // switch (type) {
- // case ControlType.TEXT:
- // new Dialog({
- // title: '文本控件',
- // data: [
- // {
- // type: 'text',
- // label: '占位符',
- // name: 'placeholder',
- // required: true,
- // placeholder: '请输入占位符'
- // },
- // {
- // type: 'text',
- // label: '默认值',
- // name: 'value',
- // placeholder: '请输入默认值'
- // }
- // ],
- // onConfirm: payload => {
- // const placeholder = payload.find(
- // p => p.name === 'placeholder'
- // )?.value
- // if (!placeholder) return
- // const value = payload.find(p => p.name === 'value')?.value || ''
- // instance.command.executeInsertElementList([
- // {
- // type: ElementType.CONTROL,
- // value: '',
- // control: {
- // type,
- // value: value
- // ? [
- // {
- // value
- // }
- // ]
- // : null,
- // placeholder
- // }
- // }
- // ])
- // }
- // })
- // break
- // case ControlType.SELECT:
- // new Dialog({
- // title: '列举控件',
- // data: [
- // {
- // type: 'text',
- // label: '占位符',
- // name: 'placeholder',
- // required: true,
- // placeholder: '请输入占位符'
- // },
- // {
- // type: 'text',
- // label: '默认值',
- // name: 'code',
- // placeholder: '请输入默认值'
- // },
- // {
- // type: 'textarea',
- // label: '值集',
- // name: 'valueSets',
- // required: true,
- // height: 100,
- // placeholder: `请输入值集JSON,例:\n[{\n"value":"有",\n"code":"98175"\n}]`
- // }
- // ],
- // onConfirm: payload => {
- // const placeholder = payload.find(
- // p => p.name === 'placeholder'
- // )?.value
- // if (!placeholder) return
- // const valueSets = payload.find(p => p.name === 'valueSets')?.value
- // if (!valueSets) return
- // const code = payload.find(p => p.name === 'code')?.value
- // instance.command.executeInsertElementList([
- // {
- // type: ElementType.CONTROL,
- // value: '',
- // control: {
- // type,
- // code,
- // value: null,
- // placeholder,
- // valueSets: JSON.parse(valueSets)
- // }
- // }
- // ])
- // }
- // })
- // break
- // case ControlType.CHECKBOX:
- // new Dialog({
- // title: '复选框控件',
- // data: [
- // {
- // type: 'text',
- // label: '默认值',
- // name: 'code',
- // placeholder: '请输入默认值,多个值以英文逗号分割'
- // },
- // {
- // type: 'textarea',
- // label: '值集',
- // name: 'valueSets',
- // required: true,
- // height: 100,
- // placeholder: `请输入值集JSON,例:\n[{\n"value":"有",\n"code":"98175"\n}]`
- // }
- // ],
- // onConfirm: payload => {
- // const valueSets = payload.find(p => p.name === 'valueSets')?.value
- // if (!valueSets) return
- // const code = payload.find(p => p.name === 'code')?.value
- // instance.command.executeInsertElementList([
- // {
- // type: ElementType.CONTROL,
- // value: '',
- // control: {
- // type,
- // code,
- // value: null,
- // valueSets: JSON.parse(valueSets)
- // }
- // }
- // ])
- // }
- // })
- // break
- // default:
- // break
- // }
- // }
- // const checkboxDom = document.querySelector(
- // '.menu-item__checkbox'
- // )
- // checkboxDom.onclick = function () {
- // console.log('checkbox')
- // instance.command.executeInsertElementList([
- // {
- // type: ElementType.CHECKBOX,
- // checkbox: {
- // value: false
- // },
- // value: ''
- // }
- // ])
- // }
- // const latexDom = document.querySelector('.menu-item__latex')
- // latexDom.onclick = function () {
- // console.log('LaTeX')
- // new Dialog({
- // title: 'LaTeX',
- // data: [
- // {
- // type: 'textarea',
- // height: 100,
- // name: 'value',
- // placeholder: '请输入LaTeX文本'
- // }
- // ],
- // onConfirm: payload => {
- // const value = payload.find(p => p.name === 'value')?.value
- // if (!value) return
- // instance.command.executeInsertElementList([
- // {
- // type: ElementType.LATEX,
- // value
- // }
- // ])
- // }
- // })
- // }
- // const dateDom = document.querySelector('.menu-item__date')
- // const dateDomOptionDom = dateDom.querySelector('.options')
- // dateDom.onclick = function () {
- // console.log('date')
- // dateDomOptionDom.classList.toggle('visible')
- // // 定位调整
- // const bodyRect = document.body.getBoundingClientRect()
- // const dateDomOptionRect = dateDomOptionDom.getBoundingClientRect()
- // if (dateDomOptionRect.left + dateDomOptionRect.width > bodyRect.width) {
- // dateDomOptionDom.style.right = '0px'
- // dateDomOptionDom.style.left = 'unset'
- // } else {
- // dateDomOptionDom.style.right = 'unset'
- // dateDomOptionDom.style.left = '0px'
- // }
- // // 当前日期
- // const date = new Date()
- // const year = date.getFullYear().toString()
- // const month = (date.getMonth() + 1).toString().padStart(2, '0')
- // const day = date.getDate().toString().padStart(2, '0')
- // const hour = date.getHours().toString().padStart(2, '0')
- // const minute = date.getMinutes().toString().padStart(2, '0')
- // const second = date.getSeconds().toString().padStart(2, '0')
- // const dateString = `${year}-${month}-${day}`
- // const dateTimeString = `${dateString} ${hour}:${minute}:${second}`
- // dateDomOptionDom.querySelector('li:first-child').innerText = dateString
- // dateDomOptionDom.querySelector('li:last-child').innerText = dateTimeString
- // }
- // dateDomOptionDom.onmousedown = function (evt) {
- // const li = evt.target
- // const dateFormat = li.dataset.format
- // dateDomOptionDom.classList.toggle('visible')
- // instance.command.executeInsertElementList([
- // {
- // type: ElementType.DATE,
- // value: '',
- // dateFormat,
- // valueList: [
- // {
- // value: li.innerText.trim()
- // }
- // ]
- // }
- // ])
- // }
- // const blockDom = document.querySelector('.menu-item__block')
- // blockDom.onclick = function () {
- // console.log('block')
- // new Dialog({
- // title: '内容块',
- // data: [
- // {
- // type: 'select',
- // label: '类型',
- // name: 'type',
- // value: 'iframe',
- // required: true,
- // options: [
- // {
- // label: '网址',
- // value: 'iframe'
- // },
- // {
- // label: '视频',
- // value: 'video'
- // }
- // ]
- // },
- // {
- // type: 'number',
- // label: '宽度',
- // name: 'width',
- // placeholder: '请输入宽度(默认页面内宽度)'
- // },
- // {
- // type: 'number',
- // label: '高度',
- // name: 'height',
- // required: true,
- // placeholder: '请输入高度'
- // },
- // {
- // type: 'textarea',
- // label: '地址',
- // height: 100,
- // name: 'value',
- // required: true,
- // placeholder: '请输入地址'
- // }
- // ],
- // onConfirm: payload => {
- // const type = payload.find(p => p.name === 'type')?.value
- // if (!type) return
- // const value = payload.find(p => p.name === 'value')?.value
- // if (!value) return
- // const width = payload.find(p => p.name === 'width')?.value
- // const height = payload.find(p => p.name === 'height')?.value
- // if (!height) return
- // const block = {
- // type: null
- // }
- // if (block.type === BlockType.IFRAME) {
- // block.iframeBlock = {
- // src: value
- // }
- // } else if (block.type === BlockType.VIDEO) {
- // block.videoBlock = {
- // src: value
- // }
- // }
- // const blockElemen = {
- // type: ElementType.BLOCK,
- // value: '',
- // height: Number(height),
- // block
- // }
- // if (width) {
- // blockElement.width = Number(width)
- // }
- // instance.command.executeInsertElementList([blockElement])
- // }
- // })
- // }
- // 5. | 搜索&替换 | 打印 |
- const searchCollapseDom = document.querySelector(
- ".menu-item__search__collapse"
- );
- const searchInputDom = document.querySelector(
- ".menu-item__search__collapse__search input"
- );
- const replaceInputDom = document.querySelector(
- ".menu-item__search__collapse__replace input"
- );
- const searchDom = document.querySelector(".menu-item__search");
- searchDom.title = `搜索与替换(${isApple ? "⌘" : "Ctrl"}+F)`;
- const searchResultDom = searchCollapseDom.querySelector(".search-result");
- function setSearchResult() {
- const result = instance.command.getSearchNavigateInfo();
- if (result) {
- const { index, count } = result;
- searchResultDom.innerText = `${index}/${count}`;
- } else {
- searchResultDom.innerText = "";
- }
- }
- searchDom.onclick = function () {
- console.log("search");
- searchCollapseDom.style.display = "block";
- const bodyRect = document.body.getBoundingClientRect();
- const searchRect = searchDom.getBoundingClientRect();
- const searchCollapseRect = searchCollapseDom.getBoundingClientRect();
- if (searchRect.left + searchCollapseRect.width > bodyRect.width) {
- searchCollapseDom.style.right = "0px";
- searchCollapseDom.style.left = "unset";
- } else {
- searchCollapseDom.style.right = "unset";
- }
- searchInputDom.focus();
- };
- searchCollapseDom.querySelector("span").onclick = function () {
- searchCollapseDom.style.display = "none";
- searchInputDom.value = "";
- replaceInputDom.value = "";
- instance.command.executeSearch(null);
- setSearchResult();
- };
- searchInputDom.oninput = function () {
- instance.command.executeSearch(searchInputDom.value || null);
- setSearchResult();
- };
- searchInputDom.onkeydown = function (evt) {
- if (evt.key === "Enter") {
- instance.command.executeSearch(searchInputDom.value || null);
- setSearchResult();
- }
- };
- searchCollapseDom.querySelector("button").onclick = function () {
- const searchValue = searchInputDom.value;
- const replaceValue = replaceInputDom.value;
- if (searchValue && replaceValue && searchValue !== replaceValue) {
- instance.command.executeReplace(replaceValue);
- }
- };
- searchCollapseDom.querySelector(".arrow-left").onclick = function () {
- instance.command.executeSearchNavigatePre();
- setSearchResult();
- };
- searchCollapseDom.querySelector(".arrow-right").onclick = function () {
- instance.command.executeSearchNavigateNext();
- setSearchResult();
- };
- // const printDom = document.querySelector('.menu-item__print')
- // printDom.title = `打印(${isApple ? '⌘' : 'Ctrl'}+P)`
- // printDom.onclick = function () {
- // console.log('print')
- // instance.command.executePrint()
- // }
- // 6. 目录显隐 | 页面模式 | 纸张缩放 | 纸张大小 | 纸张方向 | 页边距 | 全屏
- // async function updateCatalog() {
- // const catalog = await instance.command.getCatalog()
- // const catalogMainDom =
- // document.querySelector('.catalog__main')
- // catalogMainDom.innerHTML = ''
- // if (catalog) {
- // const appendCatalog = (
- // parent,
- // catalogItems
- // ) => {
- // for (let c = 0; c < catalogItems.length; c++) {
- // const catalogItem = catalogItems[c]
- // const catalogItemDom = document.createElement('div')
- // catalogItemDom.classList.add('catalog-item')
- // // 渲染
- // const catalogItemContentDom = document.createElement('div')
- // catalogItemContentDom.classList.add('catalog-item__content')
- // const catalogItemContentSpanDom = document.createElement('span')
- // catalogItemContentSpanDom.innerText = catalogItem.name
- // catalogItemContentDom.append(catalogItemContentSpanDom)
- // // 定位
- // catalogItemContentDom.onclick = () => {
- // instance.command.executeLocationCatalog(catalogItem.id)
- // }
- // catalogItemDom.append(catalogItemContentDom)
- // if (catalogItem.subCatalog && catalogItem.subCatalog.length) {
- // appendCatalog(catalogItemDom, catalogItem.subCatalog)
- // }
- // // 追加
- // parent.append(catalogItemDom)
- // }
- // }
- // appendCatalog(catalogMainDom, catalog)
- // }
- // }
- // let isCatalogShow = true
- // const catalogDom = document.querySelector('.catalog')
- // const catalogModeDom =
- // document.querySelector('.catalog-mode')
- // const catalogHeaderCloseDom = document.querySelector(
- // '.catalog__header__close'
- // )
- // const switchCatalog = () => {
- // console.log('目录', isCatalogShow)
- // isCatalogShow = !isCatalogShow
- // if (isCatalogShow) {
- // console.log('目录', isCatalogShow)
- // catalogDom.style.display = 'block'
- // updateCatalog()
- // } else {
- // catalogDom.style.display = 'none'
- // }
- // }
- // catalogModeDom.onclick = switchCatalog
- // catalogHeaderCloseDom.onclick = switchCatalog
- const pageModeDom = document.querySelector(".page-mode");
- const pageModeOptionsDom = pageModeDom.querySelector(".options");
- pageModeDom.onclick = function () {
- pageModeOptionsDom.classList.toggle("visible");
- };
- pageModeOptionsDom.onclick = function (evt) {
- const li = evt.target;
- instance.command.executePageMode(li.dataset.pageMode);
- };
- document.querySelector(".page-scale-percentage").onclick = function () {
- console.log("page-scale-recovery");
- instance.command.executePageScaleRecovery();
- };
- document.querySelector(".page-scale-minus").onclick = function () {
- console.log("page-scale-minus");
- instance.command.executePageScaleMinus();
- };
- document.querySelector(".page-scale-add").onclick = function () {
- console.log("page-scale-add");
- instance.command.executePageScaleAdd();
- };
- // 纸张大小
- const paperSizeDom = document.querySelector(".paper-size");
- const paperSizeDomOptionsDom = paperSizeDom.querySelector(".options");
- paperSizeDom.onclick = function () {
- paperSizeDomOptionsDom.classList.toggle("visible");
- };
- paperSizeDomOptionsDom.onclick = function (evt) {
- const li = evt.target;
- const paperType = li.dataset.paperSize;
- const [width, height] = paperType.split("*").map(Number);
- instance.command.executePaperSize(width, height);
- // 纸张状态回显
- paperSizeDomOptionsDom
- .querySelectorAll("li")
- .forEach((child) => child.classList.remove("active"));
- li.classList.add("active");
- };
- // 纸张方向
- const paperDirectionDom = document.querySelector(".paper-direction");
- const paperDirectionDomOptionsDom =
- paperDirectionDom.querySelector(".options");
- paperDirectionDom.onclick = function () {
- paperDirectionDomOptionsDom.classList.toggle("visible");
- };
- paperDirectionDomOptionsDom.onclick = function (evt) {
- const li = evt.target;
- const paperDirection = li.dataset.paperDirection;
- instance.command.executePaperDirection(paperDirection);
- // 纸张方向状态回显
- paperDirectionDomOptionsDom
- .querySelectorAll("li")
- .forEach((child) => child.classList.remove("active"));
- li.classList.add("active");
- };
- // 页面边距
- const paperMarginDom = document.querySelector(".paper-margin");
- paperMarginDom.onclick = function () {
- const [topMargin, rightMargin, bottomMargin, leftMargin] =
- instance.command.getPaperMargin();
- new Dialog({
- title: "页边距",
- data: [
- {
- type: "text",
- label: "上边距",
- name: "top",
- required: true,
- value: `${topMargin}`,
- placeholder: "请输入上边距",
- },
- {
- type: "text",
- label: "下边距",
- name: "bottom",
- required: true,
- value: `${bottomMargin}`,
- placeholder: "请输入下边距",
- },
- {
- type: "text",
- label: "左边距",
- name: "left",
- required: true,
- value: `${leftMargin}`,
- placeholder: "请输入左边距",
- },
- {
- type: "text",
- label: "右边距",
- name: "right",
- required: true,
- value: `${rightMargin}`,
- placeholder: "请输入右边距",
- },
- ],
- onConfirm: (payload) => {
- const top = payload.find((p) => p.name === "top")?.value;
- if (!top) return;
- const bottom = payload.find((p) => p.name === "bottom")?.value;
- if (!bottom) return;
- const left = payload.find((p) => p.name === "left")?.value;
- if (!left) return;
- const right = payload.find((p) => p.name === "right")?.value;
- if (!right) return;
- instance.command.executeSetPaperMargin([
- Number(top),
- Number(right),
- Number(bottom),
- Number(left),
- ]);
- },
- });
- };
- // 全屏
- const fullscreenDom = document.querySelector(".fullscreen");
- fullscreenDom.onclick = toggleFullscreen;
- window.addEventListener("keydown", (evt) => {
- if (evt.key === "F11") {
- toggleFullscreen();
- evt.preventDefault();
- }
- });
- document.addEventListener("fullscreenchange", () => {
- fullscreenDom.classList.toggle("exist");
- });
- function toggleFullscreen() {
- console.log("fullscreen");
- if (!document.fullscreenElement) {
- document.documentElement.requestFullscreen();
- } else {
- document.exitFullscreen();
- }
- }
- // 7. 编辑器使用模式
- let modeIndex = 1;
- const modeList = [
- {
- mode: EditorMode.READONLY,
- name: "只读模式",
- },
- {
- mode: EditorMode.EDIT,
- name: "编辑模式",
- },
- {
- mode: EditorMode.CLEAN,
- name: "清洁模式",
- },
- {
- mode: EditorMode.FORM,
- name: "表单模式",
- },
- {
- mode: EditorMode.PRINT,
- name: "打印模式",
- },
- ];
- const modeElement = document.querySelector(".editor-mode");
- // 初始设置只读模式
- const { name, mode } = modeList[modeIndex];
- modeElement.innerText = name;
- instance.command.executeMode(mode);
- // 设置菜单栏权限视觉反馈
- const isReadonly = mode === EditorMode.READONLY;
- const enableMenuList = ["search", "print"];
- document.querySelectorAll(".menu-item>div").forEach((dom) => {
- const menu = dom.dataset.menu;
- isReadonly && (!menu || !enableMenuList.includes(menu))
- ? dom.classList.add("disable")
- : dom.classList.remove("disable");
- });
- modeElement.onclick = function () {
- // 模式选择循环
- modeIndex === modeList.length - 1 ? (modeIndex = 0) : modeIndex++;
- // 设置模式
- const { name, mode } = modeList[modeIndex];
- modeElement.innerText = name;
- console.log(1212, name);
- instance.command.executeMode(mode);
- // 设置菜单栏权限视觉反馈
- const isReadonly = mode === EditorMode.READONLY;
- const enableMenuList = ["search", "print"];
- document.querySelectorAll(".menu-item>div").forEach((dom) => {
- const menu = dom.dataset.menu;
- isReadonly && (!menu || !enableMenuList.includes(menu))
- ? dom.classList.add("disable")
- : dom.classList.remove("disable");
- });
- };
- // 模拟批注
- // const commentDom = document.querySelector('.comment')
- // const updateComment = async() => {
- // const groupIds = await instance.command.getGroupIds()
- // for (const comment of this.commentList) {
- // const activeCommentDom = commentDom.querySelector(
- // `.comment-item[data-id='${comment.id}']`
- // )
- // // 编辑器是否存在对应成组id
- // if (!groupIds.includes(comment.id)) {
- // // 当前dom是否存在-不存在则追加
- // if (!activeCommentDom) {
- // const commentItem = document.createElement('div')
- // commentItem.classList.add('comment-item')
- // commentItem.setAttribute('data-id', comment.id)
- // commentItem.onclick = () => {
- // instance.command.executeLocationGroup(comment.id)
- // }
- // commentDom.append(commentItem)
- // // 选区信息
- // const commentItemTitle = document.createElement('div')
- // commentItemTitle.classList.add('comment-item__title')
- // commentItemTitle.append(document.createElement('span'))
- // const commentItemTitleContent = document.createElement('span')
- // commentItemTitleContent.innerText = comment.rangeText
- // commentItemTitle.append(commentItemTitleContent)
- // const closeDom = document.createElement('i')
- // closeDom.onclick = () => {
- // instance.command.executeDeleteGroup(comment.id)
- // }
- // commentItemTitle.append(closeDom)
- // commentItem.append(commentItemTitle)
- // // 基础信息
- // const commentItemInfo = document.createElement('div')
- // commentItemInfo.classList.add('comment-item__info')
- // const commentItemInfoName = document.createElement('span')
- // commentItemInfoName.innerText = comment.userName
- // const commentItemInfoDate = document.createElement('span')
- // commentItemInfoDate.innerText = comment.createdDate
- // commentItemInfo.append(commentItemInfoName)
- // commentItemInfo.append(commentItemInfoDate)
- // commentItem.append(commentItemInfo)
- // // 详细评论
- // const commentItemContent = document.createElement('div')
- // commentItemContent.classList.add('comment-item__content')
- // commentItemContent.innerText = comment.content
- // commentItem.append(commentItemContent)
- // commentDom.append(commentItem)
- // }
- // } else {
- // // 编辑器内不存在对应成组id则dom则移除
- // activeCommentDom?.remove()
- // }
- // }
- // }
- // 8. 内部事件监听
- instance.listener.rangeStyleChange = function (payload) {
- // 控件类型
- payload.type === ElementType.SUBSCRIPT
- ? subscriptDom.classList.add("active")
- : subscriptDom.classList.remove("active");
- payload.type === ElementType.SUPERSCRIPT
- ? superscriptDom.classList.add("active")
- : superscriptDom.classList.remove("active");
- payload.type === ElementType.SEPARATOR
- ? separatorDom.classList.add("active")
- : separatorDom.classList.remove("active");
- separatorOptionDom
- .querySelectorAll("li")
- .forEach((li) => li.classList.remove("active"));
- if (payload.type === ElementType.SEPARATOR) {
- const separator = payload.dashArray.join(",") || "0,0";
- const curSeparatorDom = separatorOptionDom.querySelector(
- `[data-separator='${separator}']`
- );
- if (curSeparatorDom) {
- curSeparatorDom.classList.add("active");
- }
- }
- // 富文本
- fontOptionDom
- .querySelectorAll("li")
- .forEach((li) => li.classList.remove("active"));
- const curFontDom = fontOptionDom.querySelector(
- `[data-family='${payload.font}']`
- );
- if (curFontDom) {
- fontSelectDom.innerText = curFontDom.innerText;
- fontSelectDom.style.fontFamily = payload.font;
- curFontDom.classList.add("active");
- }
- sizeOptionDom
- .querySelectorAll("li")
- .forEach((li) => li.classList.remove("active"));
- const curSizeDom = sizeOptionDom.querySelector(
- `[data-size='${payload.size}']`
- );
- if (curSizeDom) {
- sizeSelectDom.innerText = curSizeDom.innerText;
- curSizeDom.classList.add("active");
- } else {
- sizeSelectDom.innerText = `${payload.size}`;
- }
- payload.bold
- ? boldDom.classList.add("active")
- : boldDom.classList.remove("active");
- payload.italic
- ? italicDom.classList.add("active")
- : italicDom.classList.remove("active");
- payload.underline
- ? underlineDom.classList.add("active")
- : underlineDom.classList.remove("active");
- payload.strikeout
- ? strikeoutDom.classList.add("active")
- : strikeoutDom.classList.remove("active");
- if (payload.color) {
- colorDom.classList.add("active");
- const colorSpan = colorDom.querySelector("span");
- if (colorSpan) {
- colorSpan.style.backgroundColor = payload.color;
- }
- } else {
- colorDom.classList.remove("active");
- const colorSpan = colorDom.querySelector("span");
- if (colorSpan) {
- colorSpan.style.backgroundColor = "#000000";
- }
- }
- if (payload.highlight) {
- highlightDom.classList.add("active");
- } else {
- highlightDom.classList.remove("active");
- }
- // 行布局
- leftDom.classList.remove("active");
- centerDom.classList.remove("active");
- rightDom.classList.remove("active");
- alignmentDom.classList.remove("active");
- if (payload.rowFlex && payload.rowFlex === "right") {
- rightDom.classList.add("active");
- } else if (payload.rowFlex && payload.rowFlex === "center") {
- centerDom.classList.add("active");
- } else if (payload.rowFlex && payload.rowFlex === "alignment") {
- alignmentDom.classList.add("active");
- } else {
- leftDom.classList.add("active");
- }
- // 行间距
- rowOptionDom
- .querySelectorAll("li")
- .forEach((li) => li.classList.remove("active"));
- const curRowMarginDom = rowOptionDom.querySelector(
- `[data-rowmargin='${payload.rowMargin}']`
- );
- curRowMarginDom.classList.add("active");
- // 功能
- payload.undo
- ? undoDom.classList.remove("no-allow")
- : undoDom.classList.add("no-allow");
- payload.redo
- ? redoDom.classList.remove("no-allow")
- : redoDom.classList.add("no-allow");
- payload.painter
- ? painterDom.classList.add("active")
- : painterDom.classList.remove("active");
- // 标题
- titleOptionDom
- .querySelectorAll("li")
- .forEach((li) => li.classList.remove("active"));
- if (payload.level) {
- const curTitleDom = titleOptionDom.querySelector(
- `[data-level='${payload.level}']`
- );
- titleSelectDom.innerText = curTitleDom.innerText;
- curTitleDom.classList.add("active");
- } else {
- titleSelectDom.innerText = "正文";
- titleOptionDom.querySelector("li:first-child").classList.add("active");
- }
- // 列表
- listOptionDom
- .querySelectorAll("li")
- .forEach((li) => li.classList.remove("active"));
- if (payload.listType) {
- listDom.classList.add("active");
- const listType = payload.listType;
- const listStyle =
- payload.listType === ListType.OL
- ? ListStyle.DECIMAL
- : payload.listType;
- const curListDom = listOptionDom.querySelector(
- `[data-list-type='${listType}'][data-list-style='${listStyle}']`
- );
- if (curListDom) {
- curListDom.classList.add("active");
- }
- } else {
- listDom.classList.remove("active");
- }
- // 批注
- // commentDom
- // .querySelectorAll('.comment-item')
- // .forEach(commentItemDom => {
- // commentItemDom.classList.remove('active')
- // })
- // if (payload.groupIds) {
- // const [id] = payload.groupIds
- // const activeCommentDom = commentDom.querySelector(
- // `.comment-item[data-id='${id}']`
- // )
- // if (activeCommentDom) {
- // activeCommentDom.classList.add('active')
- // scrollIntoView(commentDom, activeCommentDom)
- // }
- // }
- };
- instance.listener.visiblePageNoListChange = function (payload) {
- const text = payload.map((i) => i + 1).join("、");
- document.querySelector(".page-no-list").innerText = text;
- };
- instance.listener.pageSizeChange = function (payload) {
- if (document.querySelector(".page-size")) {
- document.querySelector(".page-size").innerText = payload.toString();
- }
- };
- instance.listener.intersectionPageNoChange = function (payload) {
- document.querySelector(".page-no").innerText = `${payload + 1}`;
- };
- instance.listener.pageScaleChange = function (payload) {
- document.querySelector(
- ".page-scale-percentage"
- ).innerText = `${Math.floor(payload * 10 * 10)}%`;
- };
- instance.listener.controlChange = function (payload) {
- const disableMenusInControlContext = [
- "table",
- "hyperlink",
- "separator",
- "page-break",
- ];
- // 菜单操作权限
- disableMenusInControlContext.forEach((menu) => {
- const menuDom = document.querySelector(`.menu-item__${menu}`);
- payload
- ? menuDom.classList.add("disable")
- : menuDom.classList.remove("disable");
- });
- };
- instance.listener.pageModeChange = function (payload) {
- const activeMode = pageModeOptionsDom.querySelector(
- `[data-page-mode='${payload}']`
- );
- pageModeOptionsDom
- .querySelectorAll("li")
- .forEach((li) => li.classList.remove("active"));
- activeMode.classList.add("active");
- };
- const handleContentChange = async () => {
- this.$emit("isSave", true);
- // 字数
- const wordCount = await instance.command.getWordCount();
- document.querySelector(".word-count").innerText = `${wordCount || 0}`;
- // 目录
- // if (isCatalogShow) {
- // this.$nextTick(() => {
- // updateCatalog()
- // })
- // }
- // // 批注
- // this.$nextTick(() => {
- // updateComment()
- // })
- };
- instance.listener.contentChange = this.debounce(handleContentChange, 200);
- handleContentChange();
- // 9. 右键菜单注册
- instance.register.contextMenuList([
- {
- name: "批注",
- when: (payload) => {
- return (
- !payload.isReadonly &&
- payload.editorHasSelection &&
- payload.zone === EditorZone.MAIN
- );
- },
- callback: (command) => {
- new Dialog({
- title: "批注",
- data: [
- {
- type: "textarea",
- label: "批注",
- height: 100,
- name: "value",
- required: true,
- placeholder: "请输入批注",
- },
- ],
- onConfirm: (payload) => {
- const value = payload.find((p) => p.name === "value")?.value;
- if (!value) return;
- const groupId = command.executeSetGroup();
- if (!groupId) return;
- commentList.push({
- id: groupId,
- content: value,
- userName: "Hufe",
- rangeText: command.getRangeText(),
- createdDate: new Date().toLocaleString(),
- });
- },
- });
- },
- },
- {
- name: "签名",
- icon: "signature",
- when: (payload) => {
- return !payload.isReadonly && payload.editorTextFocus;
- },
- callback: (command) => {
- new Signature({
- onConfirm(payload) {
- if (!payload) return;
- const { value, width, height } = payload;
- if (!value || !width || !height) return;
- command.executeInsertElementList([
- {
- value,
- width,
- height,
- type: ElementType.IMAGE,
- },
- ]);
- },
- });
- },
- },
- {
- name: "格式整理",
- icon: "word-tool",
- when: (payload) => {
- return !payload.isReadonly;
- },
- callback: (command) => {
- command.executeWordTool();
- },
- },
- ]);
- // 10. 快捷键注册
- instance.register.shortcutList([
- {
- key: KeyMap.P,
- mod: true,
- isGlobal: true,
- callback: (command) => {
- command.executePrint();
- },
- },
- {
- key: KeyMap.F,
- mod: true,
- isGlobal: true,
- callback: (command) => {
- const text = command.getRangeText();
- searchDom.click();
- if (text) {
- searchInputDom.value = text;
- instance.command.executeSearch(text);
- setSearchResult();
- }
- },
- },
- {
- key: KeyMap.MINUS,
- ctrl: true,
- isGlobal: true,
- callback: (command) => {
- command.executePageScaleMinus();
- },
- },
- {
- key: KeyMap.EQUAL,
- ctrl: true,
- isGlobal: true,
- callback: (command) => {
- command.executePageScaleAdd();
- },
- },
- {
- key: KeyMap.ZERO,
- ctrl: true,
- isGlobal: true,
- callback: (command) => {
- command.executePageScaleRecovery();
- },
- },
- ]);
- /* // 设置初始样式
- this.$nextTick(() => {
- // 设置字体
- instance.command.executeFont(this.editorOptions.defaultFont)
- // 设置字号
- instance.command.executeSize(this.editorOptions.defaultSize)
- // 设置加粗
- if (this.editorOptions.defaultBold) {
- instance.command.executeBold()
- }
- // 设置对齐方式
- instance.command.executeAlign(this.editorOptions.defaultAlign)
- }) */
- // 在编辑器实例化后立即应用默认样式
- this.$nextTick(() => {
- if (this.editorOptions.bold !== undefined) {
- const shouldBeBold = this.editorOptions.bold !== "不加粗";
- if (shouldBeBold) {
- this.editorRef.command.executeBold();
- }
- }
- });
- // 使用setTimeout确保编辑器完全准备就绪
- setTimeout(() => {
- if (this.editorOptions.bold === "加粗") {
- try {
- // 1. 先全选所有内容
- instance.command.executeSelectAll();
-
- // 2. 执行加粗命令
- instance.command.executeBold();
-
- // 3. 取消选择
- instance.command.executeSelectAll();
-
- // 4. 将光标移到开始位置
- instance.command.executeSelect(0, 0);
- } catch (error) {
- console.error('Apply bold failed:', error);
- }
- }
- // 处理居中对齐
- if (this.editorOptions.align === "居中") {
- try {
- // 1. 先全选所有内容
- instance.command.executeSelectAll();
-
- // 2. 执行居中命令
- instance.command.executeRowFlex(RowFlex.CENTER);;
-
- // 3. 取消选择
- instance.command.executeSelectAll();
-
- // 4. 将光标移到开始位置
- instance.command.executeSelect(0, 0);
- } catch (error) {
- console.error('Apply center alignment failed:', error);
- }
- }
- }, 500); // 延迟500ms执行
- },
- };
- </script>
- <style lang="scss" >
- @import url("./components/dialog/dialog.css");
- @import url("./components/signature/signature.css");
- /* @import url("./styles.scss"); */
- .canvas-editor-wrapper {
- position: relative;
- height: 100%; // 添加固定高度
- overflow: hidden; // 防止内容溢出
- .canvas-container {
- .footer {
- position: static;
- }
- .menu {
- position: absolute;
- top: 0;
- left: 0;
- }
- .el-footer {
- background-color: #f2f4f7;
- text-align: center;
- line-height: 30px !important; /* 调整footer高度 */
- }
- .content {
- position: static;
- display: flex;
- flex-direction: column;
- max-height: 600px; //calc(100% - 90px); /* 减去header和footer的高度 */
- background-color: #f2f4f7;
- overflow: auto; /* 仅让Main区域可滚动 */
- flex-grow: 1; /* 让Main区域占据剩余空间 */
- }
- @media (max-width: 1220px) {
- .menu {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- justify-content: flex-start;
- }
- }
- #canvasEditor {
- display: flex;
- justify-content: center;
- background: #f2f4f7;
- }
- .el-header {
- position: relative;
- }
- .canvas-container {
- height: 100%;
- margin-top: 10px;
- }
- ::-webkit-scrollbar {
- height: 16px;
- width: 16px;
- overflow: visible;
- }
- ::-webkit-scrollbar-button {
- width: 0;
- height: 0;
- }
- ::-webkit-scrollbar-corner {
- background: transparent;
- }
- ::-webkit-scrollbar-thumb {
- background-color: #ddd;
- background-clip: padding-box;
- border: 4px solid #f2f4f7;
- border-radius: 8px;
- min-height: 24px;
- }
- ::-webkit-scrollbar-thumb:hover {
- background-color: #c9c9c9;
- }
- ::-webkit-scrollbar-track {
- background: #f2f4f7;
- background-clip: padding-box;
- }
- * {
- margin: 0;
- padding: 0;
- }
- ul {
- list-style: none;
- }
- .menu {
- width: 100%;
- height: 60px;
- top: 0;
- z-index: 9;
- position: absolute;
- display: flex;
- align-items: center;
- justify-content: center;
- background: #f2f4f7;
- box-shadow: 0 2px 4px 0 transparent;
- }
- .menu-divider {
- width: 1px;
- height: 16px;
- margin: 0 8px;
- display: inline-block;
- background-color: #cfd2d8;
- }
- .menu-item {
- height: 24px;
- display: flex;
- align-items: center;
- position: relative;
- }
- .menu-item > div {
- width: 24px;
- height: 24px;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- margin: 0 2px;
- }
- .menu-item > div:hover {
- background: rgba(25, 55, 88, 0.04);
- }
- .menu-item > div.active {
- background: rgba(25, 55, 88, 0.08);
- }
- .menu-item i {
- width: 16px;
- height: 16px;
- display: inline-block;
- background-repeat: no-repeat;
- background-size: 100% 100%;
- }
- .menu-item > div > span {
- width: 16px;
- height: 3px;
- display: inline-block;
- border: 1px solid #e2e6ed;
- }
- .menu-item .select {
- border: none;
- font-size: 12px;
- line-height: 24px;
- user-select: none;
- }
- .menu-item .select::after {
- position: absolute;
- content: "";
- top: 11px;
- width: 0;
- height: 0;
- right: 2px;
- border-color: #767c85 transparent transparent;
- border-style: solid solid none;
- border-width: 3px 3px 0;
- }
- .menu-item .options {
- width: 70px;
- position: absolute;
- //left: 0;
- top: 25px;
- padding: 10px;
- background: #fff;
- font-size: 14px;
- box-shadow: 0 2px 12px 0 rgb(56 56 56 / 20%);
- border: 1px solid #e2e6ed;
- border-radius: 2px;
- display: none;
- }
- .menu-item .options.visible {
- display: block;
- }
- .menu-item .options li {
- padding: 5px;
- margin: 5px 0;
- user-select: none;
- transition: all 0.3s;
- }
- .menu-item .options li:hover {
- background-color: #ebecef;
- }
- .menu-item .options li.active {
- background-color: #e2e6ed;
- }
- .menu-item .menu-item__font {
- width: 65px;
- position: relative;
- }
- .menu-item .menu-item__size {
- width: 50px;
- text-align: center;
- position: relative;
- }
- .menu-item__font .select,
- .menu-item__size .select {
- width: 100%;
- height: 100%;
- }
- .menu-item__undo.no-allow,
- .menu-item__redo.no-allow,
- .menu-item > div.disable {
- color: #c0c4cc;
- cursor: not-allowed;
- opacity: 0.4;
- pointer-events: none;
- }
- .menu-item__undo i {
- background-image: url("./assets/images/undo.svg");
- }
- .menu-item__redo i {
- background-image: url("./assets/images/redo.svg");
- }
- .menu-item__painter i {
- background-image: url("./assets/images/painter.svg");
- }
- .menu-item__format i {
- background-image: url("./assets/images/format.svg");
- }
- .menu-item__size-add i {
- background-image: url("./assets/images/size-add.svg");
- }
- .menu-item__size-minus i {
- background-image: url("./assets/images/size-minus.svg");
- }
- .menu-item__bold i {
- background-image: url("./assets/images/bold.svg");
- }
- .menu-item__italic i {
- background-image: url("./assets/images/italic.svg");
- }
- .menu-item .menu-item__underline {
- width: 30px;
- position: relative;
- }
- .menu-item__underline > i {
- flex-shrink: 0;
- background-image: url("./assets/images/underline.svg");
- }
- .menu-item__underline .select {
- width: 100%;
- height: 100%;
- }
- .menu-item .menu-item__underline .options {
- width: 128px;
- }
- .menu-item .menu-item__underline li {
- padding: 1px 5px;
- }
- .menu-item__underline li i {
- pointer-events: none;
- }
- .menu-item__underline li[data-decoration-style="solid"] {
- background-image: url("./assets/images/line-single.svg");
- }
- .menu-item__underline li[data-decoration-style="double"] {
- background-image: url("./assets/images/line-double.svg");
- }
- .menu-item__underline li[data-decoration-style="dashed"] {
- background-image: url("./assets/images/line-dash-small-gap.svg");
- }
- .menu-item__underline li[data-decoration-style="dotted"] {
- background-image: url("./assets/images/line-dot.svg");
- }
- .menu-item__underline li[data-decoration-style="wavy"] {
- background-image: url("./assets/images/line-wavy.svg");
- }
- .menu-item__strikeout i {
- background-image: url("./assets/images/strikeout.svg");
- }
- .menu-item__superscript i {
- background-image: url("./assets/images/superscript.svg");
- }
- .menu-item__subscript i {
- background-image: url("./assets/images/subscript.svg");
- }
- .menu-item__color,
- .menu-item__highlight {
- display: flex;
- flex-direction: column;
- }
- .menu-item__color #color,
- .menu-item__highlight #highlight {
- width: 1px;
- height: 1px;
- visibility: hidden;
- outline: none;
- appearance: none;
- }
- .menu-item__color i {
- background-image: url("./assets/images/color.svg");
- }
- .menu-item__color span {
- background-color: #000000;
- }
- .menu-item__highlight i {
- background-image: url("./assets/images/highlight.svg");
- }
- .menu-item__highlight span {
- background-color: #ffff00;
- }
- .menu-item .menu-item__title {
- width: 60px;
- position: relative;
- }
- .menu-item__title .select {
- width: calc(100% - 20px);
- height: 100%;
- }
- .menu-item__title i {
- transform: translateX(-5px);
- background-image: url("./assets/images/title.svg");
- }
- .menu-item__title .options {
- width: 80px;
- }
- .menu-item__left i {
- background-image: url("./assets/images/left.svg");
- }
- .menu-item__center i {
- background-image: url("./assets/images/center.svg");
- }
- .menu-item__right i {
- background-image: url("./assets/images/right.svg");
- }
- .menu-item__alignment i {
- background-image: url("./assets/images/alignment.svg");
- }
- .menu-item__justify i {
- background-image: url("./assets/images/justify.svg");
- }
- .menu-item__row-margin {
- position: relative;
- }
- .menu-item__row-margin i {
- background-image: url("./assets/images/row-margin.svg");
- }
- .menu-item__list {
- position: relative;
- }
- .menu-item__list i {
- background-image: url("./assets/images/list.svg");
- }
- .menu-item__list .options {
- width: 110px;
- }
- .menu-item__list .options > ul > li * {
- pointer-events: none;
- }
- .menu-item__list .options > ul > li li {
- margin-left: 18px;
- }
- .menu-item__list .options > ul > li[data-list-style="checkbox"] li::marker {
- font-size: 11px;
- }
- .menu-item__image i {
- background-image: url("./assets/images/image.svg");
- }
- .menu-item__image input {
- display: none;
- }
- .menu-item__table {
- position: relative;
- }
- .menu-item__table i {
- background-image: url("./assets/images/table.svg");
- }
- .menu-item .menu-item__table__collapse {
- width: 270px;
- height: 310px;
- background: #fff;
- box-shadow: 0 2px 12px 0 rgb(56 56 56 / 20%);
- border: 1px solid #e2e6ed;
- box-sizing: border-box;
- border-radius: 2px;
- position: absolute;
- display: none;
- z-index: 99;
- top: 25px;
- left: 0;
- padding: 14px 27px;
- cursor: auto;
- }
- .menu-item .menu-item__table__collapse .table-close {
- position: absolute;
- right: 10px;
- top: 5px;
- cursor: pointer;
- }
- .menu-item .menu-item__table__collapse .table-close:hover {
- color: #7d7e80;
- }
- .menu-item .menu-item__table__collapse:hover {
- background: #fff;
- }
- .menu-item .menu-item__table__collapse .table-title {
- display: flex;
- justify-content: flex-start;
- padding-bottom: 5px;
- border-bottom: 1px solid #e2e6ed;
- }
- .table-title span {
- font-size: 12px;
- color: #3d4757;
- display: inline;
- margin: 0;
- }
- .table-panel {
- cursor: pointer;
- }
- .table-panel .table-row {
- display: flex;
- flex-wrap: nowrap;
- margin-top: 10px;
- pointer-events: none;
- }
- .table-panel .table-cel {
- width: 16px;
- height: 16px;
- box-sizing: border-box;
- border: 1px solid #e2e6ed;
- background: #fff;
- position: relative;
- margin-right: 6px;
- pointer-events: none;
- }
- .table-panel .table-cel.active {
- border: 1px solid rgba(73, 145, 242, 0.2);
- background: rgba(73, 145, 242, 0.15);
- }
- .table-panel .table-row .table-cel:last-child {
- margin-right: 0;
- }
- .menu-item__hyperlink i {
- background-image: url("./assets/images/hyperlink.svg");
- }
- .menu-item__separator {
- position: relative;
- }
- .menu-item__separator > i {
- background-image: url("./assets/images/separator.svg");
- }
- .menu-item .menu-item__separator .options {
- width: 128px;
- }
- .menu-item .menu-item__separator li {
- padding: 1px 5px;
- }
- .menu-item__separator li i {
- pointer-events: none;
- }
- .menu-item__separator li[data-separator="0,0"] {
- background-image: url("./assets/images/line-single.svg");
- }
- .menu-item__separator li[data-separator="1,1"] {
- background-image: url("./assets/images/line-dot.svg");
- }
- .menu-item__separator li[data-separator="3,1"] {
- background-image: url("./assets/images/line-dash-small-gap.svg");
- }
- .menu-item__separator li[data-separator="4,4"] {
- background-image: url("./assets/images/line-dash-large-gap.svg");
- }
- .menu-item__separator li[data-separator="7,3,3,3"] {
- background-image: url("./assets/images/line-dash-dot.svg");
- }
- .menu-item__separator li[data-separator="6,2,2,2,2,2"] {
- background-image: url("./assets/images/line-dash-dot-dot.svg");
- }
- .menu-item__watermark > i {
- background-image: url("./assets/images/watermark.svg");
- }
- .menu-item__watermark {
- position: relative;
- }
- .menu-item__codeblock i {
- background-image: url("./assets/images/codeblock.svg");
- }
- .menu-item__page-break i {
- background-image: url("./assets/images/page-break.svg");
- }
- .menu-item__control {
- position: relative;
- }
- .menu-item__control i {
- background-image: url("./assets/images/control.svg");
- }
- .menu-item__checkbox i {
- background-image: url("./assets/images/checkbox.svg");
- }
- .menu-item__radio i {
- background-image: url("./assets/images/radio.svg");
- }
- .menu-item__latex i {
- background-image: url("./assets/images/latex.svg");
- }
- .menu-item__date {
- position: relative;
- }
- .menu-item__date i {
- background-image: url("./assets/images/date.svg");
- }
- .menu-item__date .options {
- width: 160px;
- }
- .menu-item__block i {
- background-image: url("./assets/images/block.svg");
- }
- .menu-item .menu-item__control .options {
- width: 55px;
- }
- .menu-item__search {
- position: relative;
- }
- .menu-item__search i {
- background-image: url("./assets/images/search.svg");
- }
- .menu-item .menu-item__search__collapse {
- width: 260px;
- height: 72px;
- box-sizing: border-box;
- position: absolute;
- display: none;
- z-index: 99;
- top: 25px;
- left: 0;
- background: #ffffff;
- box-shadow: 0px 5px 5px #e3dfdf;
- }
- .menu-item .menu-item__search__collapse:hover {
- background: #ffffff;
- }
- .menu-item .menu-item__search__collapse > div {
- width: 250px;
- height: 36px;
- padding: 0 5px;
- line-height: 36px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- border-radius: 4px;
- }
- .menu-item .menu-item__search__collapse > div input {
- width: 205px;
- height: 27px;
- appearance: none;
- background-color: #fff;
- background-image: none;
- border-radius: 4px;
- border: 1px solid #ebebeb;
- box-sizing: border-box;
- color: #606266;
- display: inline-block;
- line-height: 27px;
- outline: none;
- padding: 0 5px;
- }
- .menu-item .menu-item__search__collapse > div span {
- height: 100%;
- color: #dcdfe6;
- font-size: 25px;
- display: inline-block;
- border: 0;
- padding: 0 10px;
- }
- .menu-item .menu-item__search__collapse__replace button {
- display: inline-block;
- border: 1px solid #e2e6ed;
- border-radius: 2px;
- background: #fff;
- line-height: 22px;
- padding: 0 6px;
- white-space: nowrap;
- margin-left: 4px;
- cursor: pointer;
- font-size: 12px;
- }
- .menu-item .menu-item__search__collapse__replace button:hover {
- background: rgba(25, 55, 88, 0.04);
- }
- .menu-item .menu-item__search__collapse__search {
- position: relative;
- }
- .menu-item .menu-item__search__collapse__search label {
- right: 110px;
- font-size: 12px;
- color: #3d4757;
- position: absolute;
- }
- .menu-item .menu-item__search__collapse__search > input {
- padding: 5px 90px 5px 5px !important;
- }
- .menu-item .menu-item__search__collapse__search > div {
- width: 28px;
- height: 27px;
- display: flex;
- justify-content: center;
- align-items: center;
- position: absolute;
- border-left: 1px solid #e2e6ed;
- transition: all 0.5s;
- }
- .menu-item .menu-item__search__collapse__search > div:hover {
- background-color: rgba(25, 55, 88, 0.04);
- }
- .menu-item .menu-item__search__collapse__search i {
- width: 6px;
- height: 8px;
- transform: translateY(1px);
- }
- .menu-item .menu-item__search__collapse__search .arrow-left {
- right: 76px;
- }
- .menu-item .menu-item__search__collapse__search .arrow-left i {
- background: url(./assets/images/arrow-left.svg) no-repeat;
- }
- .menu-item .menu-item__search__collapse__search .arrow-right {
- right: 48px;
- }
- .menu-item .menu-item__search__collapse__search .arrow-right i {
- background: url(./assets/images/arrow-right.svg) no-repeat;
- }
- .menu-item__print i {
- background-image: url("./assets/images/print.svg");
- }
- .catalog {
- width: 250px;
- position: absolute;
- left: 0;
- bottom: 0;
- top: 100px;
- padding: 0 20px 40px 20px;
- }
- .catalog .catalog__header {
- height: 48px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- border-bottom: 1px solid #e2e6ed;
- }
- .catalog .catalog__header span {
- color: #3d4757;
- font-size: 14px;
- font-weight: bold;
- }
- .catalog .catalog__header i {
- width: 16px;
- height: 16px;
- cursor: pointer;
- display: inline-block;
- background: url(./assets/images/close.svg) no-repeat;
- transition: all 0.2s;
- }
- .catalog .catalog__header > div:hover {
- background: rgba(235, 238, 241);
- }
- .catalog__main {
- height: calc(100% - 60px);
- padding: 10px 0;
- overflow-y: auto;
- overflow-x: hidden;
- }
- .catalog__main .catalog-item {
- width: 100%;
- padding-left: 10px;
- box-sizing: border-box;
- }
- .catalog__main > .catalog-item {
- padding-left: 0;
- }
- .catalog__main .catalog-item .catalog-item__content {
- width: 100%;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- .catalog__main .catalog-item .catalog-item__content:hover > span {
- color: #4991f2;
- }
- .catalog__main .catalog-item .catalog-item__content span {
- color: #3d4757;
- line-height: 30px;
- font-size: 12px;
- white-space: nowrap;
- cursor: pointer;
- user-select: none;
- }
- .editor > div {
- margin: 80px auto;
- }
- .ce-page-container canvas {
- box-shadow: rgb(158 161 165 / 40%) 0px 2px 12px 0px;
- }
- .comment {
- width: 250px;
- height: 650px;
- position: absolute;
- transform: translateX(420px);
- top: 200px;
- left: 50%;
- overflow-y: auto;
- }
- .comment-item {
- background: #ffffff;
- border: 1px solid #e2e6ed;
- position: relative;
- border-radius: 8px;
- padding: 15px;
- font-size: 14px;
- margin-bottom: 20px;
- cursor: pointer;
- transition: all 0.5s;
- }
- .comment-item:hover {
- border-color: #c0c6cf;
- box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
- }
- .comment-item.active {
- border-color: #e99d00;
- box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
- }
- .comment-item__title {
- height: 22px;
- position: relative;
- display: flex;
- align-items: center;
- color: #c1c6ce;
- }
- .comment-item__title span:first-child {
- background-color: #dbdbdb;
- width: 4px;
- height: 16px;
- margin-right: 5px;
- display: inline-block;
- border-radius: 999px;
- }
- .comment-item__title span:nth-child(2) {
- width: 200px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- .comment-item__title i {
- width: 16px;
- height: 16px;
- cursor: pointer;
- position: absolute;
- right: -8px;
- top: -8px;
- background: url(./assets/images/close.svg) no-repeat;
- }
- .comment-item__title i:hover {
- opacity: 0.6;
- }
- .comment-item__info {
- height: 28px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- }
- .comment-item__info > span:first-child {
- font-weight: 600;
- }
- .comment-item__info > span:last-child {
- color: #c1c6ce;
- }
- .comment-item__content {
- line-height: 22px;
- }
- .footer {
- width: 100%;
- height: 30px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- background: #f2f4f7;
- z-index: 9;
- position: absolute;
- bottom: 0;
- left: 0;
- font-size: 12px;
- padding: 0 4px 0 20px;
- box-sizing: border-box;
- }
- .footer > div:first-child {
- display: flex;
- align-items: center;
- }
- .footer .catalog-mode {
- padding: 1px;
- position: relative;
- }
- .footer .catalog-mode i {
- width: 16px;
- height: 16px;
- margin-right: 5px;
- cursor: pointer;
- display: inline-block;
- background-image: url("./assets/images/catalog.svg");
- }
- .footer .page-mode {
- padding: 1px;
- position: relative;
- }
- .footer .page-mode i {
- width: 16px;
- height: 16px;
- margin-right: 5px;
- cursor: pointer;
- display: inline-block;
- background-image: url("./assets/images/page-mode.svg");
- }
- .footer .options {
- width: 70px;
- position: absolute;
- left: 0;
- bottom: 25px;
- padding: 10px;
- background: #fff;
- font-size: 14px;
- box-shadow: 0 2px 12px 0 rgb(56 56 56 / 20%);
- border: 1px solid #e2e6ed;
- border-radius: 2px;
- display: none;
- }
- .footer .options.visible {
- display: block;
- }
- .footer .options li {
- padding: 5px;
- margin: 5px 0;
- user-select: none;
- transition: all 0.3s;
- text-align: center;
- cursor: pointer;
- }
- .footer .options li:hover {
- background-color: #ebecef;
- }
- .footer .options li.active {
- background-color: #e2e6ed;
- }
- .footer > div:first-child > span {
- display: inline-block;
- margin-right: 5px;
- letter-spacing: 1px;
- }
- .footer > div:last-child {
- display: flex;
- align-items: center;
- justify-content: space-between;
- }
- .footer > div:last-child > div {
- width: 24px;
- height: 24px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .footer > div:last-child > div:hover {
- background: rgba(25, 55, 88, 0.04);
- }
- .footer > div:last-child i {
- width: 16px;
- height: 16px;
- display: inline-block;
- cursor: pointer;
- }
- .footer .editor-option i {
- background-image: url("./assets/images/option.svg");
- }
- .footer .page-scale-minus i {
- background-image: url("./assets/images/page-scale-minus.svg");
- }
- .footer .page-scale-add i {
- background-image: url("./assets/images/page-scale-add.svg");
- }
- .footer .page-scale-percentage {
- cursor: pointer;
- user-select: none;
- }
- .footer .fullscreen i {
- background-image: url("./assets/images/request-fullscreen.svg");
- }
- .footer .fullscreen.exist i {
- background-image: url("./assets/images/exit-fullscreen.svg");
- }
- .footer .paper-margin i {
- background-image: url("./assets/images/paper-margin.svg");
- }
- .footer .editor-mode {
- cursor: pointer;
- user-select: none;
- }
- .footer .paper-size {
- position: relative;
- }
- .footer .paper-size i {
- background-image: url("./assets/images/paper-size.svg");
- }
- .footer .paper-size .options {
- right: 0;
- left: unset;
- }
- .footer .paper-direction {
- position: relative;
- }
- .footer .paper-direction i {
- background-image: url("./assets/images/paper-direction.svg");
- }
- .footer .paper-direction .options {
- right: 0;
- left: unset;
- }
- .ce-contextmenu-signature {
- background-image: url("./assets/images/signature.svg");
- }
- .ce-contextmenu-word-tool {
- background-image: url("./assets/images/word-tool.svg");
- }
- .menu-item__color .options {
- width: 220px;
- padding: 8px;
- }
- .menu-item__color .color-section {
- margin-bottom: 10px;
- }
- .menu-item__color .section-title {
- font-size: 12px;
- color: #666;
- margin-bottom: 5px;
- padding-left: 2px;
- }
- .menu-item__color .options ul {
- display: flex;
- flex-wrap: wrap;
- gap: 2px;
- }
- .menu-item__color .options li {
- width: 18px;
- height: 18px;
- border: 1px solid #ddd;
- cursor: pointer;
- margin: 0;
- position: relative;
- }
- .menu-item__color .options li:hover {
- border-color: #000;
- }
- .menu-item__color .options li[data-color="#FFFFFF"] {
- border: 1px solid #ddd;
- }
- // 添加悬浮提示样式
- .menu-item__color .options li:hover::after {
- content: attr(title);
- position: absolute;
- bottom: 100%;
- left: 50%;
- transform: translateX(-50%);
- padding: 4px 8px;
- background-color: #333;
- color: white;
- font-size: 12px;
- border-radius: 4px;
- white-space: nowrap;
- z-index: 1000;
- }
- .menu-item__highlight .options {
- width: 180px;
- padding: 5px;
- }
- .menu-item__highlight .options ul {
- display: grid;
- grid-template-columns: repeat(6, 1fr);
- gap: 2px;
- }
- .menu-item__highlight .options li {
- width: 24px;
- height: 24px;
- border: 1px solid #ddd;
- cursor: pointer;
- margin: 0;
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #666;
- }
- /* 特殊处理"无"选项 */
- .menu-item__highlight .options li[data-color="transparent"] {
- background: white;
- font-size: 16px;
- }
- .menu-item__highlight .options li:hover {
- border-color: #000;
- }
- .menu-item__highlight .options li:hover::after {
- content: attr(title);
- position: absolute;
- bottom: 100%;
- left: 50%;
- transform: translateX(-50%);
- padding: 4px 8px;
- background-color: #333;
- color: white;
- font-size: 12px;
- border-radius: 4px;
- white-space: nowrap;
- z-index: 1000;
- }
- }
- }
- </style>
|