| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351 |
- <template>
- <div class="container">
- <van-nav-bar
- title="退货登记"
- left-arrow
- @click-left="goBack"
- @click-right="init"
- >
- <template #left>
- <van-icon name="arrow-left" size="25" />
- <div class="left-btn">返回</div>
- </template>
- <template #right>
- <div class="nav-right right-btn">重置</div>
- </template>
- </van-nav-bar>
- <div class="content">
- <div v-if="showInitialPage" class="init-container">
- <div class="scan-returned-content with-fixed-notice">
- <div class="input-group">
- <van-field
- ref="scanExpressNoInputRef"
- autofocus
- v-model="expressNo"
- autocomplete="off"
- placeholder="输入快递单号"
- clearable
- @keyup.enter="inputExpressNo"
- >
- </van-field>
- </div>
- <div class="button-group">
- <van-button
- @click="inputExpressNo"
- style="width: 100%"
- type="primary"
- class="confirm-btn"
- >确认
- </van-button>
- </div>
- </div>
- </div>
- <div v-else>
- <div class="content-tips fixed-notice">
- <van-notice-bar color="#1989fa" background="#ecf9ff"
- >{{ ownerQualityInspection }}
- </van-notice-bar>
- </div>
- <!-- 退货信息区域 -->
- <div class="return-info-section with-fixed-notice">
- <div class="section-header">
- <van-icon name="orders-o" size="18" />
- <span class="section-title">退货信息</span>
- </div>
- <van-cell-group inset style="margin-top: 10px">
- <van-field
- v-model="params.returnNo"
- label="快递单号"
- readonly
- placeholder="请输入快递单号"
- />
- <van-field
- readonly
- clickable
- label="承运商"
- placeholder="选择承运商"
- :model-value="getLogisticName()"
- @click="logisticPickerShow = true"
- />
- <van-popup
- v-model:show="logisticPickerShow"
- position="bottom"
- destroy-on-close
- >
- <van-picker
- :columns="logistics"
- @cancel="logisticPickerShow = false"
- @confirm="selectedLogistic"
- />
- </van-popup>
- <van-field
- readonly
- clickable
- label="仓库"
- placeholder="选择仓库"
- :model-value="getWarehouseName()"
- @click="warehousePickerShow = true"
- />
- <van-popup
- v-model:show="warehousePickerShow"
- position="bottom"
- destroy-on-close
- >
- <van-picker
- :columns="warehouses"
- @cancel="warehousePickerShow = false"
- @confirm="selectedWarehouse"
- />
- </van-popup>
- <van-field
- readonly
- clickable
- label="货主"
- placeholder="选择货主"
- :model-value="getOwnerName(params.ownerCode)"
- @click="showOwnerSelectFunc"
- />
- <van-field
- readonly
- clickable
- label="店铺"
- placeholder="选择店铺"
- v-model="params.storeName"
- @click="storePickerShow = true"
- />
- <van-popup
- v-model:show="storePickerShow"
- position="bottom"
- destroy-on-close
- >
- <van-picker
- :columns="storeOptions"
- @cancel="storePickerShow = false"
- @confirm="selectedStore"
- />
- </van-popup>
- <van-field
- v-model="params.remark"
- rows="1"
- autosize
- label="备注"
- type="textarea"
- placeholder="备注"
- />
- </van-cell-group>
- </div>
- <!-- 商品信息区域 -->
- <div class="product-info-section">
- <div class="section-header">
- <van-icon name="goods-collect-o" size="18" />
- <span class="section-title">商品信息</span>
- </div>
- <!-- 商品条码扫描输入框 -->
- <van-cell-group inset style="margin-top: 10px">
- <van-field
- ref="productTabScancodeInputRef"
- v-model="scancode"
- label="商品条码"
- autocomplete="off"
- placeholder="扫描或输入商品条码"
- clearable
- @keydown.enter="showQualityStatus"
- />
- </van-cell-group>
- <div class="returned-detail-list">
- <template v-if="!params.details || params.details.length === 0">
- <van-empty description="暂无信息请进行录入" />
- </template>
- <template v-for="(item, index) in params.details" :key="`detail-${item.sku || 'unknown'}-${item.barCode || 'nocode'}-${index}`">
- <div class="card-div">
- <div class="card-div-content">
- <div class="info-row">
- <div class="info-label">sku</div>
- <div class="info-value">{{ item.sku }}</div>
- <div class="info-label">质量状态</div>
- <div class="info-value">
- <van-tag :color="getTagColor(item.qualityStatus)">
- {{ item.qualityStatus }}
- </van-tag>
- </div>
- </div>
- <div class="info-row">
- <div class="info-label">商品编号</div>
- <div class="info-value">{{ item.barCode }}</div>
- <div class="info-label">商品名称</div>
- <div class="info-value">
- <van-text-ellipsis
- :content="item.tradeName"
- rows="1"
- expand-text="展开"
- collapse-text="收起"
- />
- </div>
- </div>
- <div class="info-row">
- <div class="info-label">生产日期</div>
- <div class="info-value">
- {{ item.manufactureTime }}
- </div>
- <div class="info-label">失效日期</div>
- <div class="info-value">{{ item.validityTime }}</div>
- </div>
- <div class="info-row">
- <div class="info-label">批次号</div>
- <div class="info-value">{{ item.batchNumber }}</div>
- <div class="info-label">数量</div>
- <div class="info-value">{{ item.number }}</div>
- </div>
- </div>
- <div class="card-div-footer">
- <div class="product-description">
- {{ item.remark }}
- </div>
- </div>
- <template v-if="hasBoxItems(item)">
- <div>
- <van-divider content-position="left">外箱图</van-divider>
- <van-row>
- <template v-for="(i, imgIndex) in getBoxItems(item)" :key="`box-${item.sku || 'unknown'}-${index}-${imgIndex}-${i.fileName || 'noname'}`">
- <van-col span="4">
- <van-image
- :key="`box-photos-${index}-${imgIndex}`"
- @click="
- previewImages(item.boxPhotos, imgIndex, '外箱图')
- "
- width="100%"
- height="50"
- :src="i.src"
- />
- </van-col>
- </template>
- </van-row>
- </div>
- </template>
- <template v-if="hasProductItems(item)">
- <div>
- <van-divider content-position="left">内物图</van-divider>
- <van-row>
- <template v-for="(i, imgIndex) in getProductItems(item)" :key="`product-${item.sku || 'unknown'}-${index}-${imgIndex}-${i.fileName || 'noname'}`">
- <van-col span="4">
- <van-image
- :key="`product-photos-${index}-${imgIndex}`"
- @click="
- previewImages(
- item.productPhotos,
- imgIndex,
- '内物图',
- )
- "
- width="100%"
- height="60"
- :src="i.src"
- />
- </van-col>
- </template>
- </van-row>
- </div>
- </template>
- <div class="card-div-footer-options">
- <div class="options-row">
- <div class="info-value">
- <van-button
- size="mini"
- type="danger"
- block
- plain
- @click="removeDetails(index)"
- >删除
- </van-button>
- </div>
- <div class="info-value">
- <van-button
- size="mini"
- type="default"
- block
- plain
- @click="editDetails(index)"
- >编辑
- </van-button>
- </div>
- </div>
- </div>
- </div>
- </template>
- </div>
- </div>
- <!-- 提交按钮区域 -->
- <div class="submit-section">
- <van-cell-group inset style="margin-top: 20px; margin-bottom: 50px">
- <van-button type="primary" block @click="submit">提交</van-button>
- </van-cell-group>
- </div>
- </div>
- </div>
- <van-dialog
- v-model:show="qualityStatusDialog"
- title="质量状态"
- @confirm="queryBarcode"
- show-cancel-button
- >
- <van-radio-group v-model="qualityStatus">
- <template v-for="(item, index) in qualityStatusOptions" :key="`quality-${index}`">
- <van-cell
- :title="item.text"
- clickable
- @click="qualityStatus = item.value"
- >
- <template #right-icon>
- <van-radio :name="item.value" />
- </template>
- </van-cell>
- </template>
- </van-radio-group>
- </van-dialog>
- <van-dialog
- v-model:show="returnedDetailDialog"
- title="商品详情"
- show-cancel-button
- :lazy-render="true"
- :show-confirm-button="checkUploadImages()"
- @confirm="addDetails"
- @cancel="cancelReturnedDetailDialog"
- class="compact-dialog"
- >
- <div class="dialog-content">
- <div class="field-group">
- <van-field
- v-model="selectDetail.sku"
- label="SKU"
- placeholder="SKU"
- clearable
- class="compact-field"
- />
- <van-field
- readonly
- v-model="selectDetail.barCode"
- label="商品条码"
- placeholder="商品条码"
- clearable
- class="compact-field"
- />
- <van-field
- v-model="selectDetail.tradeName"
- label="商品名称"
- placeholder="商品名称"
- readonly
- class="compact-field"
- />
- </div>
- <div class="field-group">
- <van-field
- readonly
- clickable
- label="质量状态"
- placeholder="质量状态"
- v-model="selectDetail.qualityStatus"
- @click="selectedDetailQualityStatus = true"
- class="compact-field"
- />
- </div>
- <van-popup
- v-model:show="selectedDetailQualityStatus"
- position="bottom"
- destroy-on-close
- >
- <van-picker
- :columns="qualityStatusOptions"
- @cancel="selectedDetailQualityStatus = false"
- @confirm="selectedDetailQualityStatusFunc"
- />
- </van-popup>
- <div class="field-group date-fields">
- <van-field
- is-link
- readonly
- name="datePicker"
- label="生产日期"
- :placeholder="selectDetail.manufactureTime ? '' : '请选择生产日期'"
- :model-value="formatDateDisplay(selectDetail.manufactureTime)"
- @click="showManufactureTime = true"
- class="compact-field date-field"
- />
- <van-popup
- v-model:show="showManufactureTime"
- destroy-on-close
- position="bottom"
- >
- <van-date-picker
- :max-date="maxDate"
- :min-date="minDate"
- :model-value="parseDateValue(selectDetail.manufactureTime)"
- @confirm="manufactureTimeConfirm"
- @cancel="showManufactureTime = false"
- />
- </van-popup>
- <van-field
- :model-value="formatDateDisplay(selectDetail.validityTime)"
- is-link
- readonly
- name="datePicker"
- label="失效日期"
- :placeholder="selectDetail.validityTime ? '' : '请选择失效日期'"
- @click="showValidityTime = true"
- class="compact-field date-field"
- />
- </div>
- <van-popup
- v-model:show="showValidityTime"
- destroy-on-close
- position="bottom"
- >
- <van-date-picker
- :max-date="maxDate"
- :min-date="minDate"
- :model-value="parseDateValue(selectDetail.validityTime)"
- @confirm="validityTimeConfirm"
- @cancel="showValidityTime = false"
- />
- </van-popup>
-
- <div class="field-group">
- <van-field
- autocomplete="off"
- v-model="selectDetail.batchNumber"
- label="批次号"
- placeholder="批次号"
- clearable
- class="compact-field"
- />
- <van-field
- v-model="selectDetail.number"
- label="数量"
- type="digit"
- placeholder="数量"
- clearable
- class="compact-field"
- />
- <van-field
- v-model="selectDetail.remark"
- label="备注"
- placeholder="备注"
- label-align="top"
- class="compact-field"
- />
- </div>
- <div v-if="showAccessories" class="accessories-section">
- <van-divider content-position="left" class="compact-divider">配件信息</van-divider>
- <div class="accessories-content">
- <template v-for="(item, index) in accessories" :key="`accessory-${index}`">
- <div class="accessory-item">
- 配件条码: [<span class="accessory-highlight">{{
- item.accessory
- }}</span
- >] [<span class="accessory-desc">{{ item.descrC }}</span
- >] 数量: [<span class="accessory-highlight">{{ item.qty }}</span
- >]件
- </div>
- </template>
- <div class="accessory-notice">
- 请检查商品: 【<span class="accessory-warning">{{
- selectDetail.sku
- }}</span
- >】 {{ selectDetail.tradeName }} 配件
- </div>
- </div>
- </div>
- <div v-if="selectDetail.boxPhotos && selectDetail.boxPhotos.length > 0" class="photo-section">
- <van-divider content-position="left" class="compact-divider">外箱图</van-divider>
- <div class="photo-grid">
- <template v-for="(item, index) in selectDetail.boxPhotos" :key="`detail-box-${index}`">
- <div class="photo-item">
- <van-image
- :key="`box-photos-${index}`"
- width="100%"
- height="40"
- :src="getImageUrl(item, '外箱图')"
- @click="showBoxImagePreview(index)"
- class="compact-image"
- />
- </div>
- </template>
- </div>
-
- <van-image-preview
- v-model:show="showBoxPreview"
- :images="detailBoxImages"
- :start-position="startBoxPosition"
- @change="onBoxPreviewChange"
- closeable
- >
- <template #index>
- <div class="custom-toolbar">
- <span
- >{{ startPhotosPosition + 1 }}/{{
- selectDetail.boxPhotos.length
- }}</span
- >
- <van-button
- icon="delete"
- type="danger"
- @click.stop="handleBoxDelete"
- size="mini"
- >删除
- </van-button>
- </div>
- </template>
- </van-image-preview>
- </div>
- <div v-if="selectDetail.productPhotos && selectDetail.productPhotos.length > 0" class="photo-section">
- <van-divider content-position="left" class="compact-divider">内物图</van-divider>
- <div class="photo-grid">
- <template v-for="(item, index) in selectDetail.productPhotos" :key="`detail-product-${index}`">
- <div class="photo-item">
- <van-image
- :key="`product-photos-${index}`"
- width="100%"
- height="40"
- :src="getImageUrl(item, '内物图')"
- @click="showPhotosImagePreview(index)"
- class="compact-image"
- />
- </div>
- </template>
- </div>
-
- <van-image-preview
- v-model:show="showPhotosPreview"
- :images="detailProductImages"
- :start-position="startPhotosPosition"
- @change="onPhotosPreviewChange"
- closeable
- >
- <template #index>
- <div class="custom-toolbar">
- <span>
- {{ startPhotosPosition + 1 }}/{{
- selectDetail.productPhotos.length
- }}
- </span>
- <van-button
- icon="delete"
- type="danger"
- @click.stop="handlePhotosDelete"
- size="mini"
- >删除
- </van-button>
- </div>
- </template>
- </van-image-preview>
- </div>
- <div class="camera-buttons">
- <van-button
- type="primary"
- class="camera-btn"
- @click="invokeCameraToCapture('外箱图')"
- size="small"
- >外箱图录入
- </van-button>
- <van-button
- type="primary"
- class="camera-btn"
- @click="invokeCameraToCapture('内物图')"
- size="small"
- >内物图录入
- </van-button>
- </div>
- <input
- type="file"
- id="outer-carton-box-input"
- capture="user"
- accept="image/*"
- hidden
- @change="outerCartonInput"
- />
- <input
- type="file"
- id="inner-contents-input"
- capture="user"
- accept="image/*"
- hidden
- @change="innerContentsInput"
- />
- </div>
- </van-dialog>
- <van-popup
- v-model:show="showOwnerSelect"
- destroy-on-close
- round
- position="bottom"
- >
- <van-picker
- :model-value="getOwnerName(params.ownerCode)"
- :columns="ownerSelectedOptions"
- @cancel="showOwnerSelect = false"
- @confirm="selectOwner"
- />
- </van-popup>
- <owner ref="ownerRef" @onOwner="onOwner" />
-
- <!-- 浮动按钮:仅在非初始页面显示 -->
- <van-floating-bubble
- v-show="!showInitialPage"
- icon="checked"
- @click="handleSubmitWithConfirm"
- class="submit-floating-btn"
- />
- </div>
- </template>
- <script setup>
- // ==================== Vue 核心导入 ====================
- import { ref, computed, nextTick, onMounted, watch } from 'vue'
- // ==================== Vant UI 组件导入 ====================
- import {
- showFailToast,
- showNotify,
- showLoadingToast,
- closeToast,
- showConfirmDialog,
- showImagePreview
- } from 'vant'
- // ==================== 工具类导入 ====================
- import { getHeader, goBack, scanError, scanSuccess } from '@/utils/android'
- import { useStore } from '@/store/modules/user'
- import { getStatus } from '@/utils/returned.ts'
- // ==================== API 接口导入 ====================
- import {
- deleteDetails,
- getQualityInspection,
- getQualityInspectionBy,
- getQualityStatus,
- getReturnedByExpress,
- getTagColorBy,
- listAsn,
- matchAsnBy,
- matchCarrierCode,
- matchOrderBy,
- register,
- searchBarcode,
- searchOwnerBarcode,
- shops,
- validateDate
- } from '@/api/returned/index.ts'
- import { carrierOptions, getOwner, getWarehouse } from '@/api/basic/index.ts'
- // ==================== 组件导入 ====================
- import Owner from '@/components/Owner.vue'
- // ==================== 初始化配置 ====================
- try {
- getHeader()
- } catch (error) {
- console.log(error)
- }
- // ==================== 基础配置常量 ====================
- const store = useStore()
- const warehouse = store.warehouse
- // 日期范围配置
- const minDate = ref(new Date(new Date().getFullYear() - 5, 0, 1))
- const maxDate = ref(new Date((new Date().getFullYear() + 50, 0, 1)))
- // ==================== 页面状态管理 ====================
- // 页面显示控制
- const showInitialPage = ref(true) // 是否显示初始扫描页面
- const expressNo = ref(null) // 快递单号
- // 页面标题计算属性
- const title = computed(() => {
- if (expressNo.value && showInitialPage.value === false) {
- return '退货登记:' + params.value.returnNo
- }
- return '退货登记'
- })
- // ==================== 对话框状态管理 ====================
- // 选择器对话框状态
- const logisticPickerShow = ref(false) // 承运商选择器
- const ownerPickerShow = ref(false) // 货主选择器
- const warehousePickerShow = ref(false) // 仓库选择器
- const storePickerShow = ref(false) // 店铺选择器
- const showOwnerSelect = ref(false) // 多货主选择器
- // 业务对话框状态
- const qualityStatusDialog = ref(false) // 质量状态选择对话框
- const returnedDetailDialog = ref(false) // 商品详情对话框
- const selectedDetailQualityStatus = ref(false) // 详情中的质量状态选择器
- // 日期选择器状态
- const showManufactureTime = ref(false) // 生产日期选择器
- const showValidityTime = ref(false) // 失效日期选择器
- // 其他状态
- const showAccessories = ref(false) // 配件显示状态
- // 检测是否有任何对话框显示
- const hasAnyDialogVisible = computed(() => {
- return qualityStatusDialog.value ||
- returnedDetailDialog.value ||
- logisticPickerShow.value ||
- warehousePickerShow.value ||
- storePickerShow.value ||
- ownerPickerShow.value ||
- showOwnerSelect.value ||
- showManufactureTime.value ||
- showValidityTime.value ||
- selectedDetailQualityStatus.value ||
- showAccessories.value
- })
- // ==================== 数据存储 ====================
- // 基础数据选项
- const qualityStatusOptions = ref([]) // 质量状态选项
- const owners = ref([]) // 货主列表
- const warehouses = ref([]) // 仓库列表
- const logistics = ref([]) // 承运商列表
- const storeOptions = ref([]) // 店铺选项
- const ownerSelectedOptions = ref([]) // 多货主选择选项
- // 数据映射对象
- const ownerMap = ref({}) // 货主编码到名称的映射
- const warehousesMap = ref({}) // 仓库编码到名称的映射
- const logisticsMap = ref({}) // 承运商映射
- // 业务数据
- const asnList = ref({}) // ASN单据列表
- const accessories = ref([]) // 配件信息
- const selectDetailIndex = ref(-1) // 选中的详情索引
- // 图片文件存储
- const boxFiles = ref([]) // 外箱图文件列表
- const productFiles = ref([]) // 内物图文件列表
- // ==================== 表单数据 ====================
- // 扫码相关
- const scancode = ref('') // 扫描的商品条码
- const qualityStatus = ref('') // 商品质量状态
- // 质检信息
- const qualityInspection = ref(null) // 质检配置
- const ownerQualityInspection = computed(() => {
- if (!qualityInspection.value || !params.value.ownerCode) {
- return '暂无质检信息'
- }
- if ('原单退回' === params.value.originalNo) {
- return '原单退回:快速质检'
- }
- return getQualityInspectionBy(qualityInspection.value) || '暂无质检信息'
- })
- // 其他表单字段
- const ownerName = ref('') // 货主名称
- // 主要提交参数
- const params = ref({
- id: null,
- returnNo: null,
- warehouseCode: warehouse,
- logisticsName: null,
- storeName: null,
- upstreamNo: null,
- ownerCode: null,
- orderUpstream: null,
- orderUpSteam: null,
- arrivalPayment: null,
- buyerName: null,
- asnNo: null,
- buyerPhone: null,
- originalNo: null,
- remark: null,
- details: []
- })
- // 商品详情信息
- const selectDetail = ref({
- id: null,
- rejectHeadId: null,
- rejectPushTaskNo: null,
- status: null,
- detailStatus: null,
- isGenuine: null,
- qualityMark: null,
- sku: '',
- barCode: '',
- tradeName: '',
- qualityStatus: '',
- manufactureTime: '',
- validityTime: '',
- batchNumber: '',
- remark: '',
- asnNo: null,
- number: 0,
- warehouse: null,
- warehouseBin: null,
- boxPhotos: [],
- productPhotos: [],
- files: null,
- pieceTag: null,
- createTime: null,
- creatorId: null,
- updateTime: null,
- updaterId: null,
- deleteTime: null,
- version: null,
- repairableType: null,
- repairableTypes: null,
- operatorId: null,
- operatorName: null,
- operatorTime: null,
- skuImage: null,
- orginSkuImage: null
- })
- // ==================== 组件引用 ====================
- const scanExpressNoInputRef = ref(null) // 快递单号输入框引用
- const productTabScancodeInputRef = ref(null) // 商品信息tab下的扫码输入框引用
- const ownerRef = ref(null) // 货主组件引用
- // ==================== 图片预览相关状态 ====================
- const showPhotosPreview = ref(false) // 内物图预览状态
- const startPhotosPosition = ref(0) // 内物图预览起始位置
- const showBoxPreview = ref(false) // 外箱图预览状态
- const startBoxPosition = ref(0) // 外箱图预览起始位置
- // 图片预览计算属性
- const detailBoxImages = computed(() => {
- try {
- if (!selectDetail.value || !selectDetail.value.boxPhotos) {
- return []
- }
-
- const items = selectDetail.value.boxPhotos
- if (!Array.isArray(items) || items.length === 0) {
- return []
- }
-
- if (!Array.isArray(boxFiles.value) || boxFiles.value.length === 0) {
- return []
- }
-
- return boxFiles.value
- .filter((item) => {
- return item && item.fileName && items.includes(item.fileName)
- })
- .map((item) => item.src)
- .filter(Boolean) // 过滤掉可能的undefined值
- } catch (error) {
- console.error('detailBoxImages 计算错误:', error)
- return []
- }
- })
- const detailProductImages = computed(() => {
- try {
- if (!selectDetail.value || !selectDetail.value.productPhotos) {
- return []
- }
-
- const items = selectDetail.value.productPhotos
- if (!Array.isArray(items) || items.length === 0) {
- return []
- }
-
- if (!Array.isArray(productFiles.value) || productFiles.value.length === 0) {
- return []
- }
-
- return productFiles.value
- .filter((item) => {
- return item && item.fileName && items.includes(item.fileName)
- })
- .map((item) => item.src)
- .filter(Boolean) // 过滤掉可能的undefined值
- } catch (error) {
- console.error('detailProductImages 计算错误:', error)
- return []
- }
- })
- // ==================== 生命周期钩子 ====================
- onMounted(() => {
- // 初始化日期范围
- const now = new Date()
- const currentYear = now.getFullYear()
- const currentCentury = Math.floor(currentYear / 100) * 100
- const endOfCenturyYear = currentCentury + 99
- maxDate.value = new Date(endOfCenturyYear, 0, 1)
- minDate.value = new Date(currentCentury, 0, 1)
- // 初始化基础数据
- initializeBaseData()
-
- // 初始化时自动聚焦快递单号输入框
- nextTick(() => {
- focusExpressNoInput()
- })
- })
- // ==================== 智能聚焦逻辑 ====================
- // 聚焦快递单号输入框
- function focusExpressNoInput() {
- if (scanExpressNoInputRef.value && showInitialPage.value) {
- setTimeout(() => {
- const input = scanExpressNoInputRef.value?.$el?.querySelector('input')
- if (input) {
- input.focus()
- input.setSelectionRange(0, input.value.length)
- console.log('快递单号输入框已聚焦')
- }
- }, 150) // 增加延迟时间确保 DOM 完全渲染
- }
- }
- // 聚焦商品信息输入框
- function focusProductInput() {
- if (productTabScancodeInputRef.value && !showInitialPage.value) {
- setTimeout(() => {
- const input = productTabScancodeInputRef.value?.$el?.querySelector('input')
- if (input) {
- input.focus()
- input.setSelectionRange(0, input.value.length)
- console.log('商品条码输入框已聚焦')
- }
- }, 150) // 增加延迟时间确保DOM完全渲染
- }
- }
- // 监听对话框状态变化,当所有对话框关闭时自动聚焦
- watch(hasAnyDialogVisible, (newVal) => {
- if (!newVal) {
- nextTick(() => {
- if (showInitialPage.value) {
- focusExpressNoInput()
- } else {
- focusProductInput()
- }
- })
- }
- })
- // 监听页面显示状态变化
- watch(showInitialPage, (newVal) => {
- nextTick(() => {
- if (newVal) {
- // 切换到初始扫描页面时,聚焦快递单号输入框
- setTimeout(() => {
- focusExpressNoInput()
- }, 300)
- } else {
- // 页面从初始扫描状态切换到详情页面时,延迟聚焦商品输入框
- setTimeout(() => {
- focusProductInput()
- }, 300)
- }
- })
- })
- // 监听商品详情对话框关闭,自动聚焦到商品条码输入框
- watch(returnedDetailDialog, (newVal) => {
- if (!newVal && !showInitialPage.value) {
- nextTick(() => {
- focusProductInput()
- })
- }
- })
- // ==================== 初始化方法 ====================
- // 初始化基础数据
- async function initializeBaseData() {
- try {
- // 并行获取基础数据
- const [ownersRes, qualityStatusRes, warehousesRes, logisticsRes] = await Promise.all([
- getOwner(),
- getQualityStatus(),
- getWarehouse(),
- carrierOptions()
- ])
- // 处理货主数据
- const ownersData = ownersRes.data
- owners.value = ownersData.map((item) => ({ value: item.code, text: item.name }))
- const ownerMapping = {}
- ownersData.forEach((item) => {
- ownerMapping[item.code] = item.name
- })
- ownerMap.value = ownerMapping
- // 处理质量状态数据
- qualityStatusOptions.value = qualityStatusRes.data
- .filter((item) => item !== '机损')
- .map((item) => ({ value: item, text: item }))
- // 处理仓库数据
- const warehousesData = warehousesRes.data
- warehouses.value = warehousesData.map((item) => ({ value: item.code, text: item.name }))
- const warehouseMapping = {}
- warehousesData.forEach((item) => {
- warehouseMapping[item.code] = item.name
- })
- warehousesMap.value = warehouseMapping
- // 处理承运商数据
- const logisticsData = logisticsRes.data
- logistics.value = logisticsData.map((item) => ({ value: item.name, text: item.name }))
- const logisticsMapping = {}
- logisticsData.forEach((item) => {
- logisticsMapping[item.name] = item.name
- })
- logisticsMap.value = logisticsMapping
- } catch (error) {
- console.error('初始化基础数据失败:', error)
- showFailToast('初始化数据失败,请刷新页面重试')
- }
- }
- // 初始化商品详情
- function initDetail() {
- selectDetail.value = {
- id: null,
- rejectHeadId: null,
- rejectPushTaskNo: null,
- status: null,
- detailStatus: null,
- isGenuine: null,
- qualityMark: null,
- sku: '',
- barCode: '',
- tradeName: '',
- qualityStatus: '',
- manufactureTime: '',
- validityTime: '',
- batchNumber: '',
- remark: '',
- asnNo: null,
- number: null,
- warehouse: null,
- warehouseBin: null,
- boxPhotos: [],
- productPhotos: [],
- files: null,
- pieceTag: null,
- createTime: null,
- creatorId: null,
- updateTime: null,
- updaterId: null,
- deleteTime: null,
- version: null,
- repairableType: null,
- repairableTypes: null,
- operatorId: null,
- operatorName: null,
- operatorTime: null,
- skuImage: null,
- orginSkuImage: null
- }
- }
- // 初始化质检状态
- function initQualityInspection(owner) {
- getQualityInspection(owner).then((res) => {
- qualityInspection.value = res.data
- })
- }
- // 重置提交,恢复到扫描快递单号页面
- function init(showInputPage = true) {
- ownerName.value = null
- clearImageFileCache()
- params.value = {
- orderUpstream: null,
- returnNo: null,
- warehouseCode: warehouse,
- logisticsName: null,
- storeName: null,
- orderUpSteam: null,
- arrivalPayment: null,
- buyerName: null,
- asnNo: null,
- upstreamNo: null,
- buyerPhone: null,
- originalNo: null,
- remark: null,
- details: []
- }
- storeOptions.value = []
- expressNo.value = null
- showInitialPage.value = showInputPage
- initDetail()
- }
- // ==================== 业务工具方法 ====================
- // 获取店铺信息
- async function getStoreOptionsBy(owner) {
- const results = await shops(owner)
- const { data } = results
- if (!data || data.length === 0) {
- storeOptions.value = []
- return
- }
- storeOptions.value = data.map((item) => {
- return { value: item.name, text: item.name }
- })
- }
- // 获取承运商名称
- function getLogisticName() {
- return params.value?.logisticsName || ''
- }
- // 获取货主名称
- function getOwnerName(owner) {
- if (!owner || !ownerMap.value) {
- return ''
- }
- return ownerMap.value[owner] || ''
- }
- // 获取仓库名称
- function getWarehouseName() {
- if (!params.value?.warehouseCode || !warehousesMap.value) {
- return ''
- }
- console.log(params.value.warehouseCode)
- return warehousesMap.value[params.value.warehouseCode] || ''
- }
- // 获取质量状态标签颜色
- function getTagColor(qualityStatus) {
- if (!qualityStatus) {
- return '#999999' // 默认颜色
- }
- return getTagColorBy(qualityStatus)
- }
- // 转化加密文本(长文本显示为"加密")
- function handlerLongText(text) {
- return text && text.length > 15 ? '加密' : text ? text : null
- }
- // ==================== 选择器处理方法 ====================
- // 承运商选择处理
- const selectedLogistic = (row) => {
- const { selectedOptions } = row
- logisticPickerShow.value = false
- params.value.logisticsName = selectedOptions[0].text
- }
- // 货主选择处理
- const selectedOwner = (row) => {
- const { selectedValues, selectedOptions } = row
- ownerPickerShow.value = false
- params.value.ownerCode = selectedOptions[0].value
- ownerName.value = selectedOptions[0].text
- initQualityInspection(params.value.ownerCode)
- }
- // 仓库选择处理
- const selectedWarehouse = (row) => {
- const { selectedOptions } = row
- warehousePickerShow.value = false
- params.value.warehouseCode = selectedOptions[0].value
- }
- // 店铺选择处理
- const selectedStore = (row) => {
- const { selectedOptions } = row
- storePickerShow.value = false
- params.value.storeName = selectedOptions[0].text
- }
- // 货主选择(多选场景)
- function selectOwner({ selectedValues }) {
- const ownerCode = selectedValues[0]
- params.value.ownerCode = ownerCode
- showOwnerSelect.value = false
- ownerSelectedOptions.value = []
- getStoreOptionsBy(ownerCode)
- queryOwnerBarcode(params.value.ownerCode, scancode.value)
- }
- // 显示货主选择器
- function showOwnerSelectFunc() {
- ownerRef.value?.show(1)
- }
- // 货主组件回调
- const onOwner = (item, type) => {
- params.value.ownerCode = item.code
- getStoreOptionsBy(item.code)
- }
- // 多货主选择对话框
- function showOwnerSelectDialog(items) {
- if (!items || items.length === 0) {
- ownerSelectedOptions.value = owners.value.map((item) => {
- JSON.parse(JSON.stringify(item))
- })
- showOwnerSelect.value = true
- return
- }
- ownerSelectedOptions.value = owners.value
- .filter((item) => {
- const { value } = item
- return items.includes(value)
- })
- .map((item) => JSON.parse(JSON.stringify(item)))
- showOwnerSelect.value = true
- }
- // ==================== 快递单号处理方法 ====================
- // 输入快递单号处理
- function inputExpressNo() {
- const no = expressNo.value
- const isSuccess = checkExpressNo(no)
- if (!isSuccess) {
- return
- }
- const express_no = initExpressNo(no)
- if (!express_no || express_no.length === 0) {
- showFailToast('快递单号异常')
- scanError()
- return
- }
- params.value.returnNo = express_no
- params.value.warehouseCode = warehouse
- listAsn(express_no).then((res) => {
- if (res.data) {
- asnList.value = res.data
- }
- })
- matchReturned(express_no)
- }
- // 校验快递单号格式
- function checkExpressNo(no) {
- if (!no || no.length === 0) {
- showFailToast('单号长度异常')
- scanError()
- return false
- }
- if (no.startsWith('http')) {
- showFailToast('扫描到了错误的条码')
- scanError()
- return false
- }
- if (no.endsWith('.com')) {
- showFailToast('扫描到了错误的条码')
- scanError()
- return false
- }
- const length = no.length
- if (length > 30 || length < 5) {
- showFailToast('单号长度异常')
- scanError()
- return false
- }
- return true
- }
- // 处理快递单号格式问题
- function initExpressNo(no) {
- // 处理扫描快递单号中的特殊字符和异常情况
- if (!no) {
- return no
- }
- if (no.includes('-')) {
- no = no.substring(0, no.indexOf('-')) // 京东快递异常
- }
- return no
- .replaceAll('-', '')
- .replaceAll(' ', '')
- .replaceAll('R02Z', '')
- .replaceAll('R02T', '') // 圆通退回单号异常
- }
- // ==================== 订单匹配方法 ====================
- // 匹配已存在的退货单
- function matchReturned(express_no) {
- getReturnedByExpress(express_no).then((res) => {
- const { data } = res
- if (!data) {
- matchOrder(express_no)
- return
- }
- const { id, headStatus, returnNo, warehouseCode } = data
- if (!id) {
- matchOrder(express_no)
- expressNo.value = null
- showInitialPage.value = false
- return
- }
- if (id) {
- if (['已入仓', '已拆包'].includes(headStatus)) {
- showNotify({
- type: 'primary',
- message: `当前退回单号${returnNo}--${headStatus}`
- })
- init(false)
- params.value.id = id
- params.value.returnNo = returnNo
- params.value.warehouseCode = warehouseCode
- params.value.warehousingStatus = null
- selectDetailIndex.value = -1
- matchOrder(express_no)
- expressNo.value = null
- showInitialPage.value = false
- scanSuccess()
- } else {
- showNotify({
- type: 'danger',
- message: '当前退回单号已进行过登记 如需修改请前往PC端进行'
- })
- scanSuccess()
- }
- }
- })
- }
- // 匹配原单信息
- function matchOrder(expressNo) {
- matchOrderBy(expressNo).then((res) => {
- const { data } = res
- if (!data) {
- console.log('未匹配到订单信息')
- matchAsn(expressNo)
- return
- }
- console.log('匹配到订单信息')
- if (data) {
- const {
- upstreamNo,
- shopName,
- ownerCode,
- recipientName,
- phone,
- logisticName,
- details
- } = data
- showNotify({ type: 'success', message: '匹配到原单信息' })
- params.value.upstreamNo = upstreamNo
- params.value.ownerCode = ownerCode
- params.value.warehouseCode = warehouse
- params.value.storeName = shopName
- params.value.logisticsName = logisticName
- params.value.buyerName = handlerLongText(recipientName)
- params.value.buyerPhone = handlerLongText(phone)
- params.value.originalNo = '原单退回'
- params.value.sendNo = null
- initQualityInspection(params.value.ownerCode)
- getStoreOptionsBy(ownerCode)
- if (!details || details.length === 0) {
- return
- }
- const list = []
- details.forEach((item) => {
- const {
- sku,
- barCode,
- detailAmount,
- name,
- productionDate,
- expirationDate,
- batchNumber,
- quality,
- attributeBin
- } = item
- let qualityStatus
- if (quality) {
- if (quality === 'ZP') {
- qualityStatus = '正品'
- } else if (quality === 'CP') {
- qualityStatus = '次品'
- } else {
- qualityStatus = '正品'
- }
- } else {
- qualityStatus = '正品'
- }
- list.push({
- sku: sku,
- tradeName: name,
- barCode: barCode,
- number: detailAmount,
- manufactureTime: productionDate,
- validityTime: expirationDate,
- batchNumber: batchNumber,
- warehouse: attributeBin,
- qualityStatus: qualityStatus,
- isOriginal: true
- })
- params.value.details = list
- matchCarrier(expressNo)
- })
- } else {
- matchAsn(expressNo)
- }
- })
- }
- // 匹配承运商信息
- function matchCarrier(expressNo) {
- matchCarrierCode(expressNo).then((res) => {
- const { data } = res
- if (!data || data.length === 0) {
- showNotify({
- type: 'primary',
- message: '请手动选择承运商'
- })
- } else {
- params.value.logisticsName = data
- }
- })
- }
- // 匹配ASN单信息
- function matchAsn(expressNo) {
- matchAsnBy(expressNo).then((res) => {
- const { data } = res
- if (!data) {
- console.log('未匹配asn单信息')
- matchCarrier(expressNo)
- return
- }
- const {
- ownerCode,
- phone,
- shopName,
- recipientName,
- logisticName,
- upstreamNo
- } = data
- params.value.returnNo = expressNo
- params.value.upstreamNo = upstreamNo
- params.value.ownerCode = ownerCode
- params.value.warehouseCode = warehouse
- params.value.storeName = shopName
- params.value.logisticsName = logisticName
- params.value.buyerName = handlerLongText(recipientName)
- params.value.buyerPhone = handlerLongText(phone)
- params.value.originalNo = null
- params.value.sendNo = null
- params.value.details = []
- matchCarrier(expressNo)
- getStoreOptionsBy(ownerCode)
- initQualityInspection(params.value.ownerCode)
- initDetail()
- })
- }
- // ==================== 商品信息处理工具方法 ====================
- // 检查商品是否有外箱图
- function hasBoxItems(detail) {
- try {
- if (!detail || typeof detail !== 'object') {
- return false
- }
-
- const { boxPhotos } = detail
- if (!Array.isArray(boxPhotos) || boxPhotos.length === 0) {
- return false
- }
-
- if (!Array.isArray(boxFiles.value) || boxFiles.value.length === 0) {
- return false
- }
-
- return boxFiles.value.some((item) => {
- return item && item.fileName && boxPhotos.includes(item.fileName)
- })
- } catch (error) {
- console.error('hasBoxItems 错误:', error)
- return false
- }
- }
- // 获取商品外箱图列表
- function getBoxItems(detail) {
- try {
- if (!detail || typeof detail !== 'object') {
- return []
- }
-
- const { boxPhotos } = detail
- if (!Array.isArray(boxPhotos) || boxPhotos.length === 0) {
- return []
- }
-
- if (!Array.isArray(boxFiles.value) || boxFiles.value.length === 0) {
- return []
- }
-
- return boxFiles.value.filter((item) => {
- return item && item.fileName && boxPhotos.includes(item.fileName)
- })
- } catch (error) {
- console.error('getBoxItems 错误:', error)
- return []
- }
- }
- // 检查商品是否有内物图
- function hasProductItems(detail) {
- try {
- if (!detail || typeof detail !== 'object') {
- return false
- }
-
- const { productPhotos } = detail
- if (!Array.isArray(productPhotos) || productPhotos.length === 0) {
- return false
- }
-
- if (!Array.isArray(productFiles.value) || productFiles.value.length === 0) {
- return false
- }
-
- return productFiles.value.some((item) => {
- return item && item.fileName && productPhotos.includes(item.fileName)
- })
- } catch (error) {
- console.error('hasProductItems 错误:', error)
- return false
- }
- }
- // 获取商品内物图列表
- function getProductItems(detail) {
- try {
- if (!detail || typeof detail !== 'object') {
- return []
- }
-
- const { productPhotos } = detail
- if (!Array.isArray(productPhotos) || productPhotos.length === 0) {
- return []
- }
-
- if (!Array.isArray(productFiles.value) || productFiles.value.length === 0) {
- return []
- }
-
- return productFiles.value.filter((item) => {
- return item && item.fileName && productPhotos.includes(item.fileName)
- })
- } catch (error) {
- console.error('getProductItems 错误:', error)
- return []
- }
- }
- // ==================== 数据查询方法 ====================
- // 选择质量状态
- function showQualityStatus() {
- const barcode = scancode.value
- if (!barcode) {
- showFailToast('商品条码异常')
- return
- }
- scanSuccess()
- qualityStatusDialog.value = true
- qualityStatus.value = '正品'
- }
- // 查询商品条码信息
- async function queryBarcode() {
- showLoadingToast({
- duration: 0,
- forbidClick: true,
- message: '查询商品信息中.....'
- })
- const barcode = scancode.value
- const { ownerCode, details } = params.value
- if (ownerCode) {
- const result = await queryOwnerBarcode(ownerCode, barcode)
- if (result) {
- scanSuccess()
- qualityStatusDialog.value = false
- closeToast()
- // 查询成功后清空条码输入框并聚焦
- scancode.value = ''
- setTimeout(() => {
- focusProductInput()
- }, 100)
- return
- }
- }
- if (!details || details.length === 0) {
- await queryBarcodeBy(barcode)
- qualityStatusDialog.value = false
- closeToast()
- // 查询完成后清空条码输入框并聚焦
- scancode.value = ''
- setTimeout(() => {
- focusProductInput()
- }, 100)
- }
- }
- // 根据货主查询商品条码信息
- async function queryOwnerBarcode(ownerCode, barcode) {
- try {
- // 参数验证
- if (!ownerCode || !barcode) {
- console.error('queryOwnerBarcode: 参数不完整', { ownerCode, barcode })
- return false
- }
-
- const results = await searchOwnerBarcode({ ownerCode, barcode })
-
- // 响应数据验证
- if (!results || !results.data) {
- console.error('queryOwnerBarcode: 响应数据格式错误', results)
- return false
- }
-
- const { data: { basSku } } = results
-
- if (!basSku) {
- return false
- }
-
- addDetailByBasSku(basSku)
- return true
- } catch (err) {
- closeToast()
- console.error('queryOwnerBarcode 错误:', err)
- showFailToast('查询商品信息失败')
- }
- return false
- }
- // 查询商品条码信息(适用于没有指定货主的情况)
- async function queryBarcodeBy(barcode) {
- try {
- const results = await searchBarcode({ barcode })
- const { data } = results
- const { basSku, ownerCodes } = data
- if (ownerCodes && ownerCodes.length > 1) {
- showOwnerSelectDialog(ownerCodes)
- return null
- }
- if (!basSku) {
- scanError()
- return null
- }
- addDetailByBasSku(basSku)
- scanSuccess()
- return null
- } catch (err) {
- closeToast()
- scanError()
- console.log(err.message)
- }
- scanError()
- return null
- }
- // ==================== 商品详情处理方法 ====================
- // 根据Sku信息添加商品详情
- function addDetailByBasSku(basSku) {
- // 参数安全性检查
- if (!basSku || typeof basSku !== 'object') {
- console.error('addDetailByBasSku: basSku参数无效', basSku)
- showFailToast('商品信息获取失败')
- return
- }
-
- const { sku, barCode, ownerCode, tradeName, accessories } = basSku
-
- // 必要字段验证
- if (!sku || !barCode) {
- console.error('addDetailByBasSku: 缺少必要的商品信息', { sku, barCode })
- showFailToast('商品信息不完整')
- return
- }
-
- selectDetailIndex.value = -1
- selectDetail.value.sku = sku
-
- if (
- !params.value.ownerCode ||
- !params.value.details ||
- params.value.details.length === 0
- ) {
- params.value.ownerCode = ownerCode
- if (ownerCode) {
- initQualityInspection(params.value.ownerCode)
- getStoreOptionsBy(params.value.ownerCode)
- }
- }
-
- selectDetail.value.barCode = barCode
- selectDetail.value.qualityStatus = qualityStatus.value || '正品'
- selectDetail.value.tradeName = tradeName || ''
- selectDetail.value.number = 1
- qualityStatusDialog.value = false
- returnedDetailDialog.value = true
-
- // 显示配件信息
- showAccessoriesItems(accessories)
- }
- // 显示配件信息
- function showAccessoriesItems(items) {
- if (!items || items.length === 0) {
- accessories.value = []
- showAccessories.value = false
- return
- }
- accessories.value = items
- showAccessories.value = true
- }
- // 编辑商品详情
- function editDetails(index) {
- selectDetailIndex.value = index
- const { details } = params.value
- selectDetail.value = JSON.parse(JSON.stringify(details[index]))
- returnedDetailDialog.value = true
- }
- // 删除商品详情
- function removeDetails(index) {
- if (null === index) {
- showFailToast('未找到对应的详情')
- return
- }
- showConfirmDialog({
- title: '系统提示',
- message: '是否删除当前登记信息'
- })
- .then(() => {
- const { id } = params.value.details[index]
- if (!id) {
- params.value.details.splice(index, 1)
- showNotify({ type: 'success', message: '删除成功' })
- } else {
- deleteDetails(id).then((res) => {
- if (res.data) {
- params.value.details.splice(index, 1)
- showNotify({ type: 'success', message: '删除成功' })
- } else {
- showNotify({ type: 'danger', message: '删除失败' })
- }
- })
- }
- })
- .catch(() => {
- showNotify({ type: 'primary', message: '取消删除' })
- })
- }
- // 添加商品详情到列表
- const addDetails = async () => {
- const isCheck = checkUploadImages()
- if (!isCheck) {
- returnedDetailDialog.value = true
- return false
- }
- const index = selectDetailIndex.value
- const detail = JSON.parse(JSON.stringify(selectDetail.value))
- if (index > -1) {
- params.value.details[index] = detail
- initDetail()
- // 编辑完成后聚焦到商品条码输入框
- setTimeout(() => {
- scancode.value = ''
- focusProductInput()
- }, 100)
- return true
- }
- const {
- sku: detail_sku,
- batchNumber: detail_batch_number,
- validityTime: detail_validity_time,
- manufactureTime: detail_manufacture_time,
- qualityStatus: detail_quality_status,
- number: detailNumber
- } = selectDetail.value
- if (['次品', '待修复'].includes(detail_quality_status)) {
- params.value.details.push(detail)
- initDetail()
- // 添加完成后聚焦到商品条码输入框
- setTimeout(() => {
- scancode.value = ''
- focusProductInput()
- }, 100)
- return
- }
- const searchIndex = params.value.details.findIndex((item) => {
- const { sku, batchNumber, qualityStatus, validityTime, manufactureTime } =
- item
- return (
- sku === detail_sku &&
- batchNumber === detail_batch_number &&
- detail_validity_time === validityTime &&
- detail_manufacture_time === manufactureTime &&
- detail_quality_status === qualityStatus
- )
- })
- if (searchIndex > -1) {
- showConfirmDialog({
- title: '系统提示',
- message: '查询到有对应的商品详情是否进行合并'
- })
- .then(() => {
- const { number } = params.value.details[searchIndex]
- params.value.details[searchIndex].number =
- Number(detailNumber) + Number(number)
- showNotify({ type: 'success', message: '合并成功' })
- // 合并完成后聚焦
- setTimeout(() => {
- scancode.value = ''
- focusProductInput()
- }, 100)
- })
- .catch(() => {
- params.value.details.push(detail)
- showNotify({ type: 'success', message: '添加成功' })
- // 添加完成后聚焦
- setTimeout(() => {
- scancode.value = ''
- focusProductInput()
- }, 100)
- })
- } else {
- params.value.details.push(detail)
- showNotify({ type: 'success', message: '添加成功' })
- // 添加完成后聚焦
- setTimeout(() => {
- scancode.value = ''
- focusProductInput()
- }, 100)
- }
- initDetail()
- return true
- }
- // 取消商品详情对话框
- const cancelReturnedDetailDialog = async () => {
- returnedDetailDialog.value = false
- initDetail()
- // 取消对话框后自动聚焦到商品条码输入框
- setTimeout(() => {
- focusProductInput()
- }, 100)
- }
- // ==================== 质量状态和日期处理方法 ====================
- // 质量状态选择处理
- const selectedDetailQualityStatusFunc = (row) => {
- const { selectedOptions } = row
- selectedDetailQualityStatus.value = false
- selectDetail.value.qualityStatus = selectedOptions[0].text
-
- // 质量状态变化后重新检查图片上传状态
- nextTick(() => {
- // 触发响应式更新,让对话框重新计算确认按钮状态
- checkUploadImages()
- })
- }
- // 日期显示格式化
- const formatDateDisplay = (dateString) => {
- if (!dateString) return ''
- return dateString.replace(/-/g, '/') // 将 2023-05-01 显示为 2023/05/01
- }
- // 日期字符串转数组格式 [year, month, day]
- const parseDateValue = (dateString) => {
- if (!dateString) return []
- const [year, month, day] = dateString.split('-')
- return [year, month, day]
- }
- // 生产日期确认
- const manufactureTimeConfirm = async (result) => {
- if (!result.selectedValues) {
- showManufactureTime.value = false
- return
- }
- // 正确解构参数
- const { selectedValues } = result
- const [year, month, day] = selectedValues
- const formatted_month = month.padStart(2, '0')
- const formatted_day = day.padStart(2, '0')
- const manufactureTime = `${year}-${formatted_month}-${formatted_day}`
- const { sku } = selectDetail.value
- const { ownerCode } = params.value
- const check_validate_date = await checkValidateDate(
- ownerCode,
- sku,
- manufactureTime,
- 'manufactureTime'
- )
- if (!check_validate_date) {
- console.log('检验出现异常')
- return
- }
- selectDetail.value.manufactureTime = manufactureTime
- showManufactureTime.value = false
- }
- // 失效日期确认
- const validityTimeConfirm = async (result) => {
- if (!result.selectedValues) {
- showValidityTime.value = false
- return
- }
- // 正确解构参数
- const { selectedValues } = result
- const [year, month, day] = selectedValues
- const formatted_month = month.padStart(2, '0')
- const formatted_day = day.padStart(2, '0')
- const validityTime = `${year}-${formatted_month}-${formatted_day}`
- const { sku } = selectDetail.value
- const { ownerCode } = params.value
- const check_validate_date = await checkValidateDate(
- ownerCode,
- sku,
- validityTime,
- 'validityTime'
- )
- if (!check_validate_date) {
- return
- }
- selectDetail.value.validityTime = `${year}-${formatted_month}-${formatted_day}`
- showValidityTime.value = false
- }
- // 校验日期合法性
- async function checkValidateDate(ownerCode, sku, newDate, fieldName) {
- if (['NOSKU', 'NOBARCODE'].includes(sku)) {
- return true
- }
- const type = fieldName === 'manufactureTime' ? '生产日期' : '失效日期'
- try {
- await validateDate({
- newDate: newDate,
- fieldName: fieldName,
- ownerCode: ownerCode,
- sku: sku
- })
- showNotify({
- type: 'success',
- message: `${type} 状态校验成功 `
- })
- return true
- } catch (error) {
- showNotify({
- type: 'danger',
- message: `校验${type}出现异常`
- })
- return false
- }
- }
- // ==================== 图片处理方法 ====================
- // Blob转Base64
- function blobToBase64(blob) {
- return new Promise((resolve, reject) => {
- const fileReader = new FileReader()
- fileReader.onload = (e) => {
- resolve(e.target.result)
- }
- fileReader.readAsDataURL(blob)
- fileReader.onerror = () => {
- reject(new Error('blobToBase64 error'))
- }
- })
- }
- // 获取图片URL
- function getImageUrl(file_name, type) {
- let list = []
- if (type === '外箱图') {
- list = boxFiles.value
- } else if (type === '内物图') {
- list = productFiles.value
- }
- for (let i = 0; i < list.length; i++) {
- const item = list[i]
- const { src, fileName } = item
- if (file_name === fileName) {
- return src
- }
- }
- return null
- }
- // 处理图片文件输入
- function pushImageItem(event, type) {
- const file = event.target.files[0]
- if (!file) return
- if (!file.type.startsWith('image/')) {
- alert('请选择图片文件!')
- return
- }
- const filename = file.name
- const item = {
- src: URL.createObjectURL(file),
- file: file,
- fileName: filename,
- type
- }
- if (type === '外箱图') {
- if (!selectDetail.value.boxPhotos) {
- selectDetail.value.boxPhotos = []
- }
- blobToBase64(file).then((_) => {
- const some = boxFiles.value.some((item) => {
- return filename === item.fileName
- })
- if (some) {
- showFailToast('录入重复图片')
- } else {
- boxFiles.value.push(item)
- selectDetail.value.boxPhotos.push(filename)
- }
- })
- } else if (type === '内物图') {
- if (!selectDetail.value.productPhotos) {
- selectDetail.value.productPhotos = []
- }
- blobToBase64(file).then((_) => {
- const some = productFiles.value.some((item) => {
- return filename === item.fileName
- })
- if (some) {
- showFailToast('录入重复图片')
- } else {
- productFiles.value.push(item)
- selectDetail.value.productPhotos.push(filename)
- }
- })
- }
- }
- // 触发相机拍照
- function invokeCameraToCapture(type) {
- const { sku } = selectDetail.value
- if (!sku) {
- showFailToast('当前没有可录入的商品信息')
- return
- }
- if (type === '外箱图') {
- document.getElementById('outer-carton-box-input').click()
- } else if (type === '内物图') {
- document.getElementById('inner-contents-input').click()
- }
- }
- // 外箱图文件输入事件
- function outerCartonInput(event) {
- pushImageItem(event, '外箱图')
- }
- // 内物图文件输入事件
- function innerContentsInput(event) {
- pushImageItem(event, '内物图')
- }
- // 清除图片文件缓存
- function clearImageFileCache() {
- if (productFiles.value && productFiles.value.length > 0) {
- productFiles.value
- .map((item) => item.src)
- .forEach((item) => URL.revokeObjectURL(item))
- }
- if (boxFiles.value && boxFiles.value.length > 0) {
- boxFiles.value
- .map((item) => item.src)
- .forEach((item) => URL.revokeObjectURL(item))
- }
- }
- // 校验图片上传
- function checkUploadImages() {
- const status = ['次品', '待修复']
- const { productPhotos, boxPhotos, qualityStatus } = selectDetail.value
- if (!status.includes(qualityStatus)) {
- return true
- }
- if (!qualityStatus) {
- return true
- }
- const boxPhotoIsNull = !boxPhotos || boxPhotos.length === 0
- const productPhotoIsNull = !productPhotos || productPhotos.length === 0
- if (boxPhotoIsNull) {
- showNotify({
- type: 'warning',
- message: '请传入对应的外箱图'
- })
- return false
- }
- if (productPhotoIsNull) {
- showNotify({
- type: 'warning',
- message: '请传入对应的内物图'
- })
- return false
- }
- return true
- }
- // ==================== 图片预览方法 ====================
- // 显示内物图预览
- const showPhotosImagePreview = (index) => {
- startPhotosPosition.value = index
- showPhotosPreview.value = true
- }
- // 内物图预览位置变化
- const onPhotosPreviewChange = (index) => {
- startPhotosPosition.value = index
- }
- // 删除内物图
- const handlePhotosDelete = () => {
- showConfirmDialog({
- title: '提示',
- message: '确定要删除这张图片吗?'
- })
- .then(() => {
- selectDetail.value.productPhotos.splice(startPhotosPosition.value, 1)
- showNotify({ type: 'success', message: '删除成功' })
- if (selectDetail.value.productPhotos.length === 0) {
- showPhotosPreview.value = false
- } else if (
- startPhotosPosition.value >= selectDetail.value.productPhotos.length
- ) {
- startPhotosPosition.value = selectDetail.value.productPhotos.length - 1
- }
- })
- .catch(() => {
- showNotify({ type: 'primary', message: '取消删除' })
- })
- }
- // 显示外箱图预览
- const showBoxImagePreview = (index) => {
- startBoxPosition.value = index
- showBoxPreview.value = true
- }
- // 外箱图预览位置变化
- const onBoxPreviewChange = (index) => {
- startBoxPosition.value = index
- }
- // 删除外箱图
- const handleBoxDelete = () => {
- showConfirmDialog({
- title: '提示',
- message: '确定要删除这张图片吗?'
- })
- .then(() => {
- selectDetail.value.boxPhotos.splice(startBoxPosition.value, 1)
- showNotify({ type: 'success', message: '删除成功' })
- if (selectDetail.value.boxPhotos.length === 0) {
- showBoxPreview.value = false
- } else if (
- startBoxPosition.value >= selectDetail.value.boxPhotos.length
- ) {
- startBoxPosition.value = selectDetail.value.boxPhotos.length - 1
- }
- })
- .catch(() => {
- showNotify({ type: 'primary', message: '取消删除' })
- })
- }
- // 预览图片列表
- function previewImages(filenames, index, type) {
- let list = []
- if (type === '外箱图') {
- list = boxFiles.value
- } else if (type === '内物图') {
- list = productFiles.value
- }
- const images = []
- for (let i = 0; i < list.length; i++) {
- const item = list[i]
- const { src, fileName } = item
- if (filenames.includes(fileName)) {
- images.push(src)
- }
- }
- if (!images || images.length === 0) {
- return
- }
- showImagePreview({
- images: images,
- startPosition: index
- })
- }
- // ==================== 表单提交和验证方法 ====================
- // 浮动按钮提交处理(带确认对话框)
- const handleSubmitWithConfirm = () => {
- showConfirmDialog({
- title: '提交确认',
- message: '确认提交退货登记信息吗?\n\n提交后将无法修改,请仔细检查所有信息。',
- confirmButtonText: '确认提交',
- cancelButtonText: '取消',
- confirmButtonColor: '#1989fa'
- })
- .then(() => {
- // 用户点击确认后执行提交
- submit()
- })
- .catch(() => {
- // 用户点击取消,不执行任何操作
- showNotify({
- type: 'primary',
- message: '已取消提交'
- })
- })
- }
- // 表单提交
- function submit() {
- const { ownerCode, logisticsName, returnNo, warehouseCode } = params.value
- if (!ownerCode || ownerCode.length === 0) {
- showNotify({
- type: 'warning',
- message: '没有对应的退件详情!请录入对应的退件详情'
- })
- return
- } else if (!logisticsName || logisticsName.length === 0) {
- showNotify({
- type: 'warning',
- message: '请录入承运商'
- })
- return
- } else if (!returnNo || ownerCode.length === 0) {
- showNotify({
- type: 'warning',
- message: '请录入退件单号'
- })
- return
- } else if (!warehouseCode || warehouseCode.length === 0) {
- showNotify({
- type: 'warning',
- message: '请选择登记仓库'
- })
- return
- }
- if (checkDetailNumber()) {
- return
- }
- const formData = new FormData()
- const boxItems = boxFiles && boxFiles.value ? boxFiles.value : []
- const productItems =
- productFiles && productFiles.value ? productFiles.value : []
- const files = [...boxItems, ...productItems]
- const filenames = []
- if (files && files.length > 0) {
- files.forEach((item) => {
- const { file, fileName } = item
- if (!filenames.includes(fileName)) {
- formData.append('files', file)
- filenames.push(fileName)
- }
- })
- }
- handleDetails(params.value.details)
- formData.append('body', JSON.stringify(JSON.parse(JSON.stringify(params.value))))
- showLoadingToast({
- duration: 0,
- forbidClick: true,
- message: '提交登记中.....'
- })
- register(formData)
- .then((res) => {
- const { data } = res
- const { accumulateTaskMap } = data
- closeToast()
- if (data) {
- if (accumulateTaskMap && accumulateTaskMap.length > 0) {
- let messages = []
- const ownerName = getOwnerName(params.value.ownerCode)
- for (let i = 0; i < accumulateTaskMap.length; i++) {
- const { quality, taskCode } = accumulateTaskMap[i]
- messages.push(`进入${ownerName}新的${quality}攒单任务号${taskCode}`)
- }
- showConfirmDialog({
- title: '提交成功',
- message: messages.join('\r\n'),
- theme: 'round-button'
- })
- } else {
- showNotify({
- type: 'success',
- message: '成功提交'
- })
- }
- init()
- params.value.ownerCode = ownerCode
- }
- })
- .catch(() => {
- closeToast()
- })
- }
- // 检查详情数量
- function checkDetailNumber() {
- const { details } = params.value
- if (!details || details.length === 0) {
- showNotify({
- type: 'warning',
- message: '登记详情不能为空'
- })
- return true
- }
- const check = details.some((item) => {
- const { number } = item
- return Number.isNaN(number) || Number(number) <= 0
- })
- if (check) {
- showNotify({
- type: 'warning',
- message: '登记数量不能为 0 和 空 '
- })
- }
- return check
- }
- // 处理详情数据
- function handleDetails(details) {
- for (let i = 0; i < details.length; i++) {
- const { qualityStatus } = details[i]
- const { isGenuineValue, qualityMark } = getStatus(qualityStatus)
- details[i].isGenuine = isGenuineValue
- details[i].qualityMark = qualityMark
- }
- }
- // ==================== 图片处理方法 ====================
- // ==================== 全局事件处理 ====================
- // 页面刷新事件
- window.onRefresh = async () => {
- console.log('window.onRefresh')
- }
- </script>
- <style scoped lang="sass">
- // ==================== 导航栏样式 ====================
- .van-nav-bar
- position: fixed
- top: 0
- left: 0
- right: 0
- width: 100%
- z-index: 1000
- background: linear-gradient(135deg, #1989fa 0%, #1976d2 100%)
- box-shadow: 0 2px 8px rgba(25, 137, 250, 0.3)
-
- .left-btn
- color: #fff
- height: 46px
- padding-right: 20px
- line-height: 46px
- font-size: 15px
- font-weight: 500
- transition: opacity 0.3s ease
- &:active
- opacity: 0.7
- .right-btn
- color: #fff
- font-size: 15px
- font-weight: 500
- transition: opacity 0.3s ease
- &:active
- opacity: 0.7
- // ==================== 主容器样式 ====================
- .container
- min-height: 100vh
- background: linear-gradient(180deg, #f7f8fa 0%, #ffffff 100%)
- overflow-x: hidden // 防止横向滚动
- max-width: 100vw // 限制最大宽度
- box-sizing: border-box // 确保padding不增加总宽度
- .init-container
- width: 100%
- max-width: 100%
- padding: 16px 8px
- box-sizing: border-box
- margin-top: 10px // 确保不被导航栏遮挡
- min-height: calc(100vh - 100px) // 确保有足够的显示空间
- flex-direction: column
- justify-content: center // 垂直居中显示内容
- .content-tips
- margin-bottom: 10px
- border-radius: 8px
- overflow: hidden
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08)
- max-width: 100%
- box-sizing: border-box
- display: block // 确保块级显示
- width: 100% // 明确设置宽度
-
- :deep(.van-notice-bar)
- width: 100%
- box-sizing: border-box
- word-wrap: break-word
- white-space: normal // 允许文字换行
-
- .van-notice-bar__content
- flex: 1
- min-width: 0 // 允许flex元素正确缩小
- word-break: break-word
- overflow-wrap: break-word
-
- .van-notice-bar__text
- word-break: break-word
- overflow-wrap: break-word
- white-space: normal
- // 固定定位的提示条样式
- &.fixed-notice
- position: fixed
- top: 46px // 导航栏高度
- left: 0
- right: 0
- z-index: 999
- background: white
- margin: 0
- padding: 8px 16px
- border-bottom: 1px solid #ebedf0
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1)
- border-radius: 0
- max-width: 100vw
- box-sizing: border-box
- .scan-returned-content
- padding: 16px
- background: #fff
- border-radius: 16px
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08)
- margin-top: 8px
- max-width: 100%
- box-sizing: border-box
-
- // 当有固定提示条时的上边距
- &.with-fixed-notice
- margin-top: 65px // 与退货信息区域保持一致
- .input-group
- padding: 4px 0
- margin-bottom: 16px
- max-width: 100%
- box-sizing: border-box
- :deep(.van-field)
- border-radius: 12px
- border: 1px solid #ebedf0
- transition: border-color 0.3s ease, box-shadow 0.3s ease
- max-width: 100%
- box-sizing: border-box
- &:focus-within
- border-color: #1989fa
- box-shadow: 0 0 0 2px rgba(25, 137, 250, 0.1)
- .van-field__control
- word-break: break-all
- overflow-wrap: break-word
- .button-group
- padding: 4px 0
- max-width: 100%
- box-sizing: border-box
- .confirm-btn
- height: 44px
- border-radius: 12px
- font-size: 16px
- font-weight: 600
- background: linear-gradient(135deg, #1989fa 0%, #1976d2 100%)
- border: none
- box-shadow: 0 4px 12px rgba(25, 137, 250, 0.3)
- transition: all 0.3s ease
- max-width: 100%
- box-sizing: border-box
- &:active
- transform: translateY(1px)
- box-shadow: 0 2px 8px rgba(25, 137, 250, 0.4)
- .content
- width: 100%
- max-width: 100vw
- margin-top: 46px
- min-height: calc(100vh - 46px)
- background: #f7f8fa
- padding-bottom: 20px
- overflow-x: hidden
- box-sizing: border-box
- padding-top: 10px // 为初始扫描页面添加上边距
- // ==================== 固定定位提示条样式 ====================
- .content-tips.fixed-notice
- position: fixed
- top: 46px // 导航栏高度
- left: 0
- right: 0
- z-index: 999
- background: white
- margin: 0
- padding: 8px 16px
- border-bottom: 1px solid #ebedf0
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1)
- border-radius: 0
- max-width: 100vw
- box-sizing: border-box
- min-height: auto // 允许高度自适应
-
- :deep(.van-notice-bar)
- width: 100%
- box-sizing: border-box
- word-wrap: break-word
- white-space: normal // 允许文字换行
-
- .van-notice-bar__content
- flex: 1
- min-width: 0
- word-break: break-word
- overflow-wrap: break-word
-
- .van-notice-bar__text
- word-break: break-word
- overflow-wrap: break-word
- white-space: normal
- line-height: 1.4
- // ==================== 区块头部样式 ====================
- .section-header
- display: flex
- align-items: center
- padding: 12px 16px
- background: linear-gradient(135deg, #1989fa 0%, #1976d2 100%)
- color: white
- margin: 8px
- border-radius: 12px 12px 0 0
- box-shadow: 0 2px 8px rgba(25, 137, 250, 0.3)
- .section-title
- margin-left: 8px
- font-size: 16px
- font-weight: 600
- // ==================== 退货信息区域 ====================
- .return-info-section
- margin-bottom: 20px
- // 当有固定提示条时的上边距
- &.with-fixed-notice
- margin-top: 65px // 优化上边距:导航栏46px + 提示条约15px + 安全间距4px
- .content-tips
- margin: 8px
- border-radius: 0 0 12px 12px
- overflow: hidden
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06)
- // ==================== 商品信息区域 ====================
- .product-info-section
- margin-bottom: 20px
- // ==================== 提交区域 ====================
- .submit-section
- margin-top: 20px
- // ==================== 内容提示区域 ====================
- .content-tips
- border-radius: 12px
- overflow: hidden
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06)
- width: 100%
- box-sizing: border-box
- :deep(.van-notice-bar)
- border-radius: 12px
- font-size: 14px
- font-weight: 500
- width: 100%
- box-sizing: border-box
-
- .van-notice-bar__content
- flex: 1
- min-width: 0
- word-break: break-word
- overflow-wrap: break-word
- white-space: normal
-
- .van-notice-bar__text
- word-break: break-word
- overflow-wrap: break-word
- white-space: normal
- line-height: 1.5
- // ==================== 表单组件优化 ====================
- :deep(.van-cell-group)
- border-radius: 16px
- overflow: hidden
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06)
- margin: 8px
- max-width: calc(100% - 16px)
- box-sizing: border-box
- .van-cell
- padding: 12px 16px
- border-bottom: 1px solid #f5f7fa
- transition: background-color 0.3s ease
- max-width: 100%
- box-sizing: border-box
- overflow: hidden
- &:last-child
- border-bottom: none
- &:active
- background-color: #f8f9fa
- .van-field__label
- font-weight: 600
- color: #323233
- font-size: 14px
- white-space: nowrap
- overflow: hidden
- text-overflow: ellipsis
- .van-field__control
- font-size: 14px
- color: #646566
- word-break: break-all
- overflow-wrap: break-word
- // ==================== 商品卡片样式优化 ====================
- .scan-returned-no
- align-items: center
- padding: 12px
- .returned-detail-list
- padding: 0 8px
- max-width: 100%
- box-sizing: border-box
- .card-div
- background: #fff
- border-radius: 16px
- overflow: hidden
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08)
- margin: 8px 0
- border: 1px solid #f0f0f0
- transition: all 0.3s ease
- max-width: 100%
- box-sizing: border-box
- &:hover
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12)
- transform: translateY(-1px)
- .card-div-content
- padding: 12px
- max-width: 100%
- box-sizing: border-box
- .info-row
- display: flex
- align-items: center
- margin-bottom: 10px
- font-size: 13px
- line-height: 1.4
- max-width: 100%
- overflow: hidden
- &:last-child
- margin-bottom: 0
- .info-label
- width: 80px
- min-width: 80px
- color: #969799
- font-weight: 500
- font-size: 12px
- flex-shrink: 0
- white-space: nowrap
- overflow: hidden
- text-overflow: ellipsis
- .info-value
- flex: 1
- color: #323233
- font-weight: 600
- font-size: 13px
- margin-left: 8px
- word-break: break-all
- overflow-wrap: break-word
- min-width: 0 // 允许flex元素缩小到内容大小
- :deep(.van-tag)
- font-size: 11px
- padding: 2px 8px
- border-radius: 6px
- font-weight: 600
- max-width: 100%
- box-sizing: border-box
- :deep(.van-text-ellipsis)
- font-size: 13px
- line-height: 1.4
- max-width: 100%
- .card-div-footer
- padding: 10px 12px
- background: linear-gradient(135deg, #f8f9fa 0%, #f5f7fa 100%)
- border-top: 1px solid #ebedf0
- color: #646566
- font-size: 13px
- line-height: 1.5
- .product-description
- color: #646566
- font-style: italic
- .card-div-footer-images
- padding: 10px 12px
- background: #fafbfc
- border-top: 1px solid #ebedf0
- :deep(.van-divider)
- margin: 6px 0
- font-size: 12px
- color: #969799
- :deep(.van-image)
- border-radius: 8px
- overflow: hidden
- border: 1px solid #ebedf0
- transition: all 0.3s ease
- &:hover
- transform: scale(1.05)
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15)
- .card-div-footer-options
- background: #f8f9fa
- border-top: 1px solid #ebedf0
- padding: 10px 12px
- .options-row
- display: flex
- gap: 12px
- .info-value
- flex: 1
- :deep(.van-button)
- height: 32px
- border-radius: 8px
- font-size: 13px
- font-weight: 600
- transition: all 0.3s ease
- &.van-button--danger
- border-color: #ee0a24
- color: #ee0a24
- &:active
- background-color: #fee
- &.van-button--default
- border-color: #1989fa
- color: #1989fa
- &:active
- background-color: #e6f4ff
- // ==================== 对话框优化 ====================
- :deep(.van-dialog)
- border-radius: 16px
- overflow: hidden
- .van-dialog__header
- padding: 16px 20px 12px
- font-size: 18px
- font-weight: 600
- .van-dialog__content
- padding: 0 20px 16px
- max-height: 70vh
- overflow-y: auto
- &::-webkit-scrollbar
- width: 4px
- &::-webkit-scrollbar-thumb
- background: #c8c9cc
- border-radius: 2px
- .van-dialog__footer
- padding: 12px 20px 16px
- border-top: 1px solid #ebedf0
- .van-button
- height: 40px
- border-radius: 8px
- font-weight: 600
- // ==================== 对话框优化 ====================
- :deep(.van-dialog)
- border-radius: 16px
- overflow: hidden
-
- &.compact-dialog
- .van-dialog__header
- padding: 12px 16px
- font-size: 16px
- font-weight: 600
-
- .van-dialog__content
- padding: 0
-
- .van-dialog__footer
- padding: 8px 16px
-
- .van-button
- height: 36px
- font-size: 14px
- border-radius: 8px
-
- // ==================== 紧凑型对话框内容样式 ====================
- .dialog-content
- max-height: 75vh
- overflow-y: auto
- padding: 8px 12px
-
- .field-group
- margin-bottom: 8px
-
- &.date-fields
- display: flex
- flex-direction: column
- gap: 4px
-
- .compact-field
- margin-bottom: 4px !important
-
- :deep(.van-cell)
- padding: 8px 12px
- min-height: 40px
-
- :deep(.van-field__label)
- font-size: 13px
- font-weight: 500
- width: 60px
- color: #646566
-
- :deep(.van-field__control)
- font-size: 13px
-
- &.date-field
- :deep(.van-field__label)
- width: 65px
-
- .compact-divider
- margin: 8px 0 6px 0 !important
- font-size: 13px
- font-weight: 600
-
- :deep(.van-divider__content)
- padding: 0 8px
-
- // 配件信息样式
- .accessories-section
- margin: 8px 0
-
- .accessories-content
- padding: 6px 8px
- background: #f8f9fa
- border-radius: 8px
-
- .accessory-item
- font-size: 11px
- line-height: 1.4
- margin-bottom: 4px
-
- .accessory-highlight
- color: #2ca547
- font-weight: 600
-
- .accessory-desc
- color: #277b39
-
- .accessory-notice
- font-size: 11px
- margin-top: 6px
- padding-top: 6px
- border-top: 1px solid #ebedf0
-
- .accessory-warning
- color: #ff2020
- font-weight: 600
-
- // 图片区域样式
- .photo-section
- margin: 8px 0
-
- .photo-grid
- display: grid
- grid-template-columns: repeat(6, 1fr)
- gap: 6px
- padding: 6px 0
-
- .photo-item
- .compact-image
- border-radius: 6px
- border: 1px solid #ebedf0
- transition: transform 0.2s ease, box-shadow 0.2s ease
-
- &:hover
- transform: scale(1.05)
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15)
-
- // 拍照按钮样式
- .camera-buttons
- display: flex
- gap: 8px
- margin-top: 12px
- padding-top: 8px
- border-top: 1px solid #f0f0f0
-
- .camera-btn
- flex: 1
- height: 36px
- font-size: 13px
- border-radius: 8px
- font-weight: 500
- // ==================== 图片预览优化 ====================
- :deep(.van-image-preview)
- .custom-toolbar
- position: absolute
- top: 20px
- right: 20px
- display: flex
- align-items: center
- gap: 12px
- padding: 8px 12px
- background: rgba(0, 0, 0, 0.6)
- border-radius: 20px
- color: white
- font-size: 14px
- z-index: 1000
- .van-button
- border-radius: 6px
- font-size: 12px
- padding: 4px 8px
- // ==================== 浮动按钮样式 ====================
- .submit-floating-btn
- position: fixed
- bottom: 80px
- right: 20px
- z-index: 999
-
- :deep(.van-floating-bubble)
- background: linear-gradient(135deg, #1989fa 0%, #1976d2 100%)
- box-shadow: 0 4px 12px rgba(25, 137, 250, 0.4)
- transition: all 0.3s ease
-
- &:hover
- transform: scale(1.05)
- box-shadow: 0 6px 16px rgba(25, 137, 250, 0.5)
-
- &:active
- transform: scale(0.95)
-
- .van-icon
- color: white
- font-size: 18px
- // ==================== 弹出层优化 ====================
- :deep(.van-popup)
- border-radius: 16px 16px 0 0
- overflow: hidden
- :deep(.van-picker)
- .van-picker__toolbar
- padding: 12px 16px
- background: #f8f9fa
- .van-picker__confirm
- color: #1989fa
- font-weight: 600
- .van-picker__cancel
- color: #969799
- font-weight: 600
- // ==================== 响应式优化 ====================
- // 小屏幕设备适配 (iPhone SE, 小屏幕手机)
- @media (max-width: 375px)
- // 初始扫描页面优化
- .init-container
- padding: 12px 6px
- margin-top: 15px // 增加上边距确保不被遮挡
- min-height: calc(100vh - 120px) // 调整最小高度
-
- .content-tips
- margin-bottom: 12px
-
- :deep(.van-notice-bar)
- font-size: 13px
- padding: 8px 12px
-
- .van-notice-bar__content
- min-width: 0
-
- .van-notice-bar__text
- font-size: 13px
- line-height: 1.4
-
- .scan-returned-content
- padding: 16px 12px
- margin-top: 8px
- border-radius: 12px
-
- // 小屏幕固定提示条适配
- &.with-fixed-notice
- margin-top: 60px // 小屏幕适配:减少上边距
-
- .input-group
- margin-bottom: 12px
-
- :deep(.van-field)
- .van-field__label
- font-size: 13px
-
- .van-field__control
- font-size: 14px
-
- .button-group
- .confirm-btn
- height: 40px
- font-size: 15px
-
- // 退货信息区域优化
- .return-info-section
- &.with-fixed-notice
- margin-top: 60px // 小屏幕适配:减少上边距
-
- // 商品卡片优化
- .returned-detail-list
- .card-div
- margin: 6px 0
-
- .card-div-content
- padding: 10px
-
- .info-row
- font-size: 12px
- margin-bottom: 8px
-
- .info-label
- width: 70px
- font-size: 11px
-
- .info-value
- font-size: 12px
-
- // 导航栏优化
- .van-nav-bar
- .left-btn
- font-size: 14px
- padding-right: 16px
-
- .right-btn
- font-size: 14px
-
- // 区块头部优化
- .section-header
- padding: 10px 14px
- margin: 6px
-
- .section-title
- font-size: 15px
-
- // 紧凑型对话框小屏幕优化
- .dialog-content
- padding: 6px 8px
-
- .field-group
- margin-bottom: 6px
-
- .compact-field
- :deep(.van-cell)
- padding: 6px 8px
- min-height: 36px
-
- :deep(.van-field__label)
- font-size: 12px
- width: 55px
-
- :deep(.van-field__control)
- font-size: 12px
-
- .photo-section
- .photo-grid
- grid-template-columns: repeat(5, 1fr)
- gap: 4px
-
- .camera-buttons
- gap: 6px
-
- .camera-btn
- height: 32px
- font-size: 12px
-
- // 浮动按钮小屏幕适配
- .submit-floating-btn
- bottom: 70px
- right: 16px
-
- :deep(.van-floating-bubble)
- width: 50px
- height: 50px
-
- .van-icon
- font-size: 16px
-
- // 中等屏幕设备适配 (iPhone 6/7/8)
- @media (max-width: 414px) and (min-width: 376px)
- .init-container
- padding: 16px 8px
- margin-top: 20px // 适当增加上边距
- min-height: calc(100vh - 110px)
-
- .scan-returned-content
- padding: 18px 14px
-
- // 中等屏幕固定提示条适配
- &.with-fixed-notice
- margin-top: 62px // 中等屏幕适中的上边距
-
- .input-group
- :deep(.van-field)
- .van-field__control
- font-size: 15px
-
- .button-group
- .confirm-btn
- height: 42px
- font-size: 16px
-
- // 中等屏幕退货信息区域优化
- .return-info-section
- &.with-fixed-notice
- margin-top: 62px // 中等屏幕适中的上边距
-
- // 紧凑型对话框中等屏幕优化
- .dialog-content
- padding: 8px 10px
-
- .compact-field
- :deep(.van-field__label)
- font-size: 13px
- width: 60px
-
- :deep(.van-field__control)
- font-size: 13px
-
- // 中等屏幕浮动按钮适配
- .submit-floating-btn
- bottom: 75px
- right: 18px
-
- // 大屏幕设备适配 (iPhone Plus, 大屏手机)
- @media (min-width: 415px)
- .init-container
- max-width: 500px
- margin: 25px auto 0 auto // 增加上边距并居中
- padding: 20px 10px
- min-height: calc(100vh - 130px)
-
- .scan-returned-content
- padding: 24px 18px
-
- // 大屏幕固定提示条适配
- &.with-fixed-notice
- margin-top: 65px // 大屏幕标准上边距
-
- .input-group
- :deep(.van-field)
- .van-field__control
- font-size: 16px
-
- .button-group
- .confirm-btn
- height: 46px
- font-size: 17px
- max-width: 300px
- margin: 0 auto
-
- // 大屏幕浮动按钮适配
- .submit-floating-btn
- bottom: 80px
- right: 20px
-
- :deep(.van-floating-bubble)
- width: 56px
- height: 56px
-
- .van-icon
- font-size: 20px
-
- // 横屏适配
- @media (orientation: landscape) and (max-height: 500px)
- .content
- padding-top: 5px // 横屏时减少上边距
-
- .init-container
- padding: 8px 4px
- margin-top: 5px
- min-height: calc(100vh - 80px)
- justify-content: flex-start // 横屏时顶部对齐而非居中
-
- .content-tips
- margin-bottom: 8px
-
- .scan-returned-content
- padding: 12px 10px
-
- .input-group
- margin-bottom: 8px
-
- .button-group
- .confirm-btn
- height: 36px
-
- // 横屏退货信息区域优化
- .return-info-section
- &.with-fixed-notice
- margin-top: 55px // 横屏模式使用更紧凑的上边距
- // ==================== 空状态优化 ====================
- :deep(.van-empty)
- padding: 30px 20px
-
- .van-empty__image
- width: 100px
- height: 100px
-
- .van-empty__description
- color: #969799
- font-size: 14px
- margin-top: 12px
- // ==================== 按钮通用样式 ====================
- :deep(.van-button)
- transition: all 0.3s ease
- max-width: 100%
- box-sizing: border-box
-
- &:active
- transform: scale(0.98)
-
- &.van-button--primary
- background: linear-gradient(135deg, #1989fa 0%, #1976d2 100%)
- border: none
- box-shadow: 0 2px 8px rgba(25, 137, 250, 0.3)
-
- &:active
- box-shadow: 0 4px 12px rgba(25, 137, 250, 0.4)
- // ==================== 全局防溢出规则 ====================
- *
- box-sizing: border-box
-
- body, html
- overflow-x: hidden
- max-width: 100vw
-
- // 防止长文本溢出
- .van-field__control input,
- .van-field__control textarea,
- :deep(.van-field__control input),
- :deep(.van-field__control textarea)
- word-break: break-all
- overflow-wrap: break-word
- max-width: 100%
-
- // 防止图片溢出
- :deep(.van-image img)
- max-width: 100%
- height: auto
-
- // 防止容器溢出
- .van-cell-group,
- .van-cell,
- .card-div,
- :deep(.van-cell-group),
- :deep(.van-cell),
- :deep(.card-div)
- max-width: 100%
- overflow: hidden
- box-sizing: border-box
- </style>
|