detail.vue 98 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198
  1. <template>
  2. <div class="app-container">
  3. <!-- 页面头部 -->
  4. <div class="detail-header">
  5. <el-icon class="back-icon" @click="goBack"><ArrowLeft /></el-icon>
  6. <span class="header-title">查看供应商</span>
  7. </div>
  8. <!-- 标签页 -->
  9. <el-tabs v-model="activeTab" class="detail-tabs">
  10. <!-- 基本信息 -->
  11. <el-tab-pane label="基本信息" name="basic">
  12. <BasicInfoTab
  13. v-model:selectedOfficeRegion="selectedOfficeRegion"
  14. :detailData="detailData"
  15. :companyOptions="companyOptions"
  16. :enterpriseScaleOptions="enterpriseScaleOptions"
  17. :industryCategoryOptions="industryCategoryOptions"
  18. :supplierLevelOptions="supplierLevelOptions"
  19. :supplierTypeOptions="supplierTypeOptions"
  20. :isAddMode="isAddMode"
  21. :isViewMode="isViewMode"
  22. :isBasicInfoSaved="isBasicInfoSaved"
  23. :businessInfo="businessInfo"
  24. :businessInfoLoading="businessInfoLoading"
  25. :regionOptions="regionOptions"
  26. :paymentInfoList="paymentInfoList"
  27. :formatDate="formatDate"
  28. @save="handleSave"
  29. @getBusinessInfo="handleGetBusinessInfo"
  30. @officeRegionChange="handleOfficeRegionChange"
  31. @addPayment="handleAddPayment"
  32. @viewPayment="handleViewPayment"
  33. @editPayment="handleEditPayment"
  34. />
  35. </el-tab-pane>
  36. <!-- 采购信息 -->
  37. <el-tab-pane label="采销信息" name="purchase" :disabled="isAddMode && !isBasicInfoSaved">
  38. <PurchaseInfoTab
  39. v-model:selectedProductManager="selectedProductManager"
  40. v-model:selectedBuyer="selectedBuyer"
  41. :staffOptions="staffOptions"
  42. :isViewMode="isViewMode"
  43. @save="handleSavePurchaseInfo"
  44. />
  45. </el-tab-pane>
  46. <!-- 联系人 -->
  47. <el-tab-pane label="联系人" name="contact" :disabled="isAddMode && !isBasicInfoSaved">
  48. <ContactTab
  49. :contactSearchParams="contactSearchParams"
  50. :contactList="contactList"
  51. :contactLoading="contactLoading"
  52. :isViewMode="isViewMode"
  53. @search="handleContactSearch"
  54. @reset="handleContactReset"
  55. @add="handleAddContact"
  56. @view="handleViewContact"
  57. @edit="handleEditContact"
  58. />
  59. </el-tab-pane>
  60. <!-- 供应信息 -->
  61. <el-tab-pane label="供应信息" name="supply" :disabled="isAddMode && !isBasicInfoSaved">
  62. <SupplyInfoTab
  63. v-model:selectedCategories="selectedCategories"
  64. :isViewMode="isViewMode"
  65. :productCategoryList="productCategoryList"
  66. :selectedBrands="selectedBrands"
  67. :supplyAreaList="supplyAreaList"
  68. :authorizationList="authorizationList"
  69. :authorizationPagination="authorizationPagination"
  70. :formatDate="formatDate"
  71. :getAuthorizedStatusText="getAuthorizedStatusText"
  72. @saveCategories="handleSaveCategories"
  73. @addBrand="handleAddBrand"
  74. @removeBrand="handleRemoveBrand"
  75. @editSupplyArea="handleEditSupplyArea"
  76. @authorizationPagination="getAuthorizationList"
  77. />
  78. </el-tab-pane>
  79. <!-- 地址管理 -->
  80. <el-tab-pane label="地址管理" name="address" :disabled="isAddMode && !isBasicInfoSaved">
  81. <AddressTab
  82. :addressList="addressList"
  83. :isViewMode="isViewMode"
  84. @add="handleAddAddress"
  85. @edit="handleEditAddress"
  86. @delete="handleDeleteAddress"
  87. />
  88. </el-tab-pane>
  89. <!-- 合同管理 -->
  90. <el-tab-pane label="合同管理" name="contract" :disabled="isAddMode && !isBasicInfoSaved">
  91. <ContractTab
  92. :contractSearchParams="contractSearchParams"
  93. :contractList="contractList"
  94. :contractPagination="contractPagination"
  95. :isViewMode="isViewMode"
  96. :contract-type-dict="contractTypeDict"
  97. :contract-status-dict="contractStatusDict"
  98. :formatDate="formatDate"
  99. :getContractTypeText="getContractTypeText"
  100. :getContractStatusText="getContractStatusText"
  101. @search="handleContractSearch"
  102. @reset="handleContractReset"
  103. @add="handleAddContract"
  104. @viewAttachment="handleViewAttachment"
  105. @view="handleViewContract"
  106. @edit="handleEditContract"
  107. @sizeChange="handleContractSizeChange"
  108. @currentChange="handleContractCurrentChange"
  109. />
  110. </el-tab-pane>
  111. </el-tabs>
  112. <!-- 付款信息对话框 -->
  113. <el-dialog
  114. v-model="paymentDialogVisible"
  115. :title="paymentDialogTitle"
  116. width="1000px"
  117. :close-on-click-modal="false"
  118. >
  119. <el-form
  120. ref="paymentFormRef"
  121. :model="paymentForm"
  122. :rules="paymentFormRules"
  123. label-width="140px"
  124. :disabled="paymentDialogReadonly"
  125. >
  126. <el-form-item label="开票类型:" prop="invoiceTypeNo">
  127. <el-select
  128. v-model="paymentForm.invoiceTypeNo"
  129. placeholder="请选择"
  130. style="width: 100%;"
  131. @change="handleInvoiceTypeChange"
  132. >
  133. <el-option
  134. v-for="item in invoiceTypeList"
  135. :key="item.id"
  136. :label="item.invoiceTypeName"
  137. :value="item.id"
  138. />
  139. </el-select>
  140. </el-form-item>
  141. <el-form-item label="发票抬头:" prop="businessName">
  142. <el-input v-model="paymentForm.businessName" placeholder="企业工商名称" disabled/>
  143. </el-form-item>
  144. <el-form-item label="纳税人识别号:" prop="circlesName">
  145. <el-input v-model="paymentForm.circlesName" placeholder="请输入纳税人识别号" disabled/>
  146. </el-form-item>
  147. <el-form-item label="开户行行号:" prop="bankNum">
  148. <el-input v-model="paymentForm.bankNum" placeholder="请输入开户行行号" />
  149. </el-form-item>
  150. <el-form-item label="开户行名称:" prop="bankInfoNo">
  151. <el-select
  152. v-model="paymentForm.bankInfoNo"
  153. placeholder="请选择"
  154. style="width: 100%;"
  155. @change="handleBankChange"
  156. >
  157. <el-option
  158. v-for="item in systemBankList"
  159. :key="item.id"
  160. :label="item.bnName"
  161. :value="item.id"
  162. />
  163. </el-select>
  164. </el-form-item>
  165. <el-form-item label="银行账户:" prop="bankNo">
  166. <el-input v-model="paymentForm.bankNo" placeholder="请输入银行账户" />
  167. </el-form-item>
  168. <el-form-item label="固定电话:" prop="phone">
  169. <el-input v-model="paymentForm.phone" placeholder="请输入固定电话" />
  170. </el-form-item>
  171. <el-form-item label="地址:" prop="businessAddress">
  172. <el-input v-model="paymentForm.businessAddress" placeholder="工商地址" disabled />
  173. </el-form-item>
  174. <el-form-item label="是否主账号:" prop="num">
  175. <el-radio-group v-model="paymentForm.num">
  176. <el-radio :label="1">是</el-radio>
  177. <el-radio :label="0">否</el-radio>
  178. </el-radio-group>
  179. </el-form-item>
  180. </el-form>
  181. <template #footer>
  182. <div class="dialog-footer">
  183. <el-button @click="paymentDialogVisible = false">取消</el-button>
  184. <el-button v-if="!paymentDialogReadonly" type="primary" @click="handlePaymentSubmit" :loading="paymentSubmitLoading">确定</el-button>
  185. </div>
  186. </template>
  187. </el-dialog>
  188. <!-- 联系人对话框 -->
  189. <el-dialog
  190. v-model="contactDialogVisible"
  191. :title="contactDialogTitle"
  192. width="900px"
  193. :close-on-click-modal="false"
  194. >
  195. <el-form
  196. ref="contactFormRef"
  197. :model="contactForm"
  198. :rules="contactFormRules"
  199. label-width="140px"
  200. :disabled="contactDialogReadonly"
  201. >
  202. <el-row :gutter="20">
  203. <el-col :span="12">
  204. <el-form-item label="员工姓名:" prop="userName">
  205. <el-input v-model="contactForm.userName" placeholder="请输入员工姓名" />
  206. </el-form-item>
  207. </el-col>
  208. <el-col :span="12">
  209. <el-form-item label="手机号:" prop="phone">
  210. <el-input
  211. v-model="contactForm.phone"
  212. placeholder="请输入手机号"
  213. maxlength="11"
  214. @input="onContactPhoneInput"
  215. />
  216. </el-form-item>
  217. </el-col>
  218. </el-row>
  219. <el-row :gutter="20">
  220. <el-col :span="12">
  221. <el-form-item label="部门:" prop="departmentNo">
  222. <el-input v-model="contactForm.departmentNo" placeholder="请输入部门" />
  223. </el-form-item>
  224. </el-col>
  225. <el-col :span="12">
  226. <el-form-item label="职位:" prop="position">
  227. <el-input v-model="contactForm.position" placeholder="请输入职位" />
  228. </el-form-item>
  229. </el-col>
  230. </el-row>
  231. <el-row :gutter="20">
  232. <el-col :span="12">
  233. <el-form-item label="角色:" prop="roleNo">
  234. <el-input v-model="contactForm.roleNo" placeholder="请输入角色" />
  235. </el-form-item>
  236. </el-col>
  237. <el-col :span="12">
  238. <el-form-item label="邮箱:" prop="email">
  239. <el-input v-model="contactForm.email" placeholder="请输入邮箱" />
  240. </el-form-item>
  241. </el-col>
  242. </el-row>
  243. <el-row :gutter="20">
  244. <el-col :span="12">
  245. <el-form-item label="主要联系人:" prop="isPrimaryContact">
  246. <el-select v-model="contactForm.isPrimaryContact" placeholder="请选择" style="width: 100%;">
  247. <el-option label="是" value="1" />
  248. <el-option label="否" value="0" />
  249. </el-select>
  250. </el-form-item>
  251. </el-col>
  252. <el-col :span="12">
  253. <el-form-item label="允许登录供应商端:" prop="isRegister">
  254. <el-select v-model="contactForm.isRegister" placeholder="请选择" style="width: 100%;">
  255. <el-option label="是" value="1" />
  256. <el-option label="否" value="0" />
  257. </el-select>
  258. </el-form-item>
  259. </el-col>
  260. </el-row>
  261. <el-row :gutter="20">
  262. <el-col :span="12">
  263. <el-form-item label="传真:" prop="fax">
  264. <el-input v-model="contactForm.fax" placeholder="请输入传真" />
  265. </el-form-item>
  266. </el-col>
  267. </el-row>
  268. <el-row :gutter="20">
  269. <el-col :span="24">
  270. <el-form-item label="备注:" prop="remark">
  271. <el-input
  272. v-model="contactForm.remark"
  273. type="textarea"
  274. :rows="3"
  275. placeholder="请输入备注"
  276. />
  277. </el-form-item>
  278. </el-col>
  279. </el-row>
  280. </el-form>
  281. <template #footer>
  282. <div class="dialog-footer">
  283. <el-button @click="contactDialogVisible = false">取消</el-button>
  284. <el-button v-if="!contactDialogReadonly" type="primary" @click="handleContactSubmit" :loading="contactSubmitLoading">确定</el-button>
  285. </div>
  286. </template>
  287. </el-dialog>
  288. <!-- 供货区域编辑对话框 -->
  289. <el-dialog
  290. v-model="supplyAreaDialogVisible"
  291. title="编辑供货区域"
  292. width="700px"
  293. :close-on-click-modal="false"
  294. >
  295. <RegionCascader v-model="selectedSupplyAreaCodes" :multiple="true" :options="supplyAreaOptions" :show-district="false" />
  296. <template #footer>
  297. <div class="dialog-footer">
  298. <el-button @click="supplyAreaDialogVisible = false">取消</el-button>
  299. <el-button type="primary" @click="handleSupplyAreaSubmit" :loading="supplyAreaSubmitLoading">确定</el-button>
  300. </div>
  301. </template>
  302. </el-dialog>
  303. <!-- 地址管理对话框 -->
  304. <el-dialog
  305. v-model="addressDialogVisible"
  306. :title="addressDialogTitle"
  307. width="650px"
  308. :close-on-click-modal="false"
  309. >
  310. <el-form
  311. ref="addressFormRef"
  312. :model="addressForm"
  313. :rules="addressFormRules"
  314. label-width="120px"
  315. :disabled="addressDialogReadonly"
  316. >
  317. <el-row :gutter="20">
  318. <el-col :span="12">
  319. <el-form-item label="供应商编号:">
  320. <el-input v-model="addressForm.supplierNo" disabled />
  321. </el-form-item>
  322. </el-col>
  323. <el-col :span="12">
  324. <el-form-item label="收货人:" prop="shipperName" required>
  325. <el-input v-model="addressForm.shipperName" placeholder="请输入收货人" />
  326. </el-form-item>
  327. </el-col>
  328. </el-row>
  329. <el-row :gutter="20">
  330. <el-col :span="12">
  331. <el-form-item label="手机号码:" prop="shipperPhone" required>
  332. <el-input v-model="addressForm.shipperPhone" placeholder="请输入手机号码" />
  333. </el-form-item>
  334. </el-col>
  335. <el-col :span="12">
  336. <el-form-item label="邮政编码:" prop="shippingPostCode">
  337. <el-input v-model="addressForm.shippingPostCode" placeholder="请输入邮政编码" />
  338. </el-form-item>
  339. </el-col>
  340. </el-row>
  341. <el-row :gutter="20">
  342. <el-col :span="24">
  343. <el-form-item label="地址:" prop="shippingProvincial" required>
  344. <el-cascader
  345. v-model="selectedAddressRegion"
  346. :options="regionOptions"
  347. placeholder="请选择"
  348. clearable
  349. filterable
  350. style="width: 100%;"
  351. @change="handleAddressRegionChange"
  352. />
  353. </el-form-item>
  354. </el-col>
  355. </el-row>
  356. <el-row :gutter="20">
  357. <el-col :span="24">
  358. <el-form-item label="详细地址:" prop="shippingAddress" required>
  359. <el-input
  360. v-model="addressForm.shippingAddress"
  361. type="textarea"
  362. :rows="3"
  363. placeholder="请输入详细地址"
  364. />
  365. </el-form-item>
  366. </el-col>
  367. </el-row>
  368. <el-row :gutter="20">
  369. <el-col :span="12">
  370. <el-form-item label="默认地址:">
  371. <el-switch v-model="addressForm.isSelf" :active-value="1" :inactive-value="0" />
  372. </el-form-item>
  373. </el-col>
  374. </el-row>
  375. </el-form>
  376. <template #footer>
  377. <div class="dialog-footer">
  378. <el-button @click="addressDialogVisible = false">取消</el-button>
  379. <el-button v-if="!addressDialogReadonly" type="primary" @click="handleAddressSubmit" :loading="addressSubmitLoading">确定</el-button>
  380. </div>
  381. </template>
  382. </el-dialog>
  383. <!-- 合同管理对话框 -->
  384. <el-dialog
  385. v-model="contractDialogVisible"
  386. :title="contractDialogTitle"
  387. width="900px"
  388. :close-on-click-modal="false"
  389. >
  390. <el-form
  391. ref="contractFormRef"
  392. :model="contractForm"
  393. :rules="contractFormRules"
  394. label-width="140px"
  395. :disabled="contractDialogReadonly"
  396. >
  397. <el-row :gutter="20">
  398. <el-col :span="24">
  399. <el-form-item label="合同名称:" prop="contractName">
  400. <el-input v-model="contractForm.contractName" placeholder="请输入合同名称" />
  401. </el-form-item>
  402. </el-col>
  403. </el-row>
  404. <el-row :gutter="20">
  405. <el-col :span="12">
  406. <el-form-item label="合同类型:" prop="contractType">
  407. <el-select v-model="contractForm.contractType" placeholder="请选择" style="width: 100%;">
  408. <el-option
  409. v-for="item in contractTypeDict"
  410. :key="item.dictValue"
  411. :label="item.dictLabel"
  412. :value="item.dictValue"
  413. />
  414. </el-select>
  415. </el-form-item>
  416. </el-col>
  417. <el-col :span="12">
  418. <el-form-item label="提醒时间:" prop="demandReminderTime">
  419. <el-input-number
  420. v-model="contractForm.demandReminderTime"
  421. :min="1"
  422. :max="365"
  423. style="width: 150px;"
  424. />
  425. <span style="margin-left: 10px;">天</span>
  426. </el-form-item>
  427. </el-col>
  428. </el-row>
  429. <el-row :gutter="20">
  430. <el-col :span="12">
  431. <el-form-item label="开始时间:" prop="contractStartTime">
  432. <el-date-picker
  433. v-model="contractForm.contractStartTime"
  434. type="date"
  435. placeholder="请选择"
  436. style="width: 100%;"
  437. />
  438. </el-form-item>
  439. </el-col>
  440. <el-col :span="12">
  441. <el-form-item label="截止时间:" prop="contractEndTime">
  442. <el-date-picker
  443. v-model="contractForm.contractEndTime"
  444. type="date"
  445. placeholder="请选择"
  446. style="width: 100%;"
  447. />
  448. </el-form-item>
  449. </el-col>
  450. </el-row>
  451. <el-row :gutter="20">
  452. <el-col :span="12">
  453. <el-form-item label="开票类型:" prop="invoiceType">
  454. <el-select v-model="contractForm.invoiceType" placeholder="请选择" style="width: 100%;">
  455. <el-option
  456. v-for="item in invoiceTypeList"
  457. :key="item.id"
  458. :label="item.invoiceTypeName"
  459. :value="item.id"
  460. />
  461. </el-select>
  462. </el-form-item>
  463. </el-col>
  464. <el-col :span="12">
  465. <el-form-item label="合同金额:" prop="contractAmount">
  466. <el-input v-model="contractForm.contractAmount" placeholder="请输入">
  467. <template #append>万元</template>
  468. </el-input>
  469. </el-form-item>
  470. </el-col>
  471. </el-row>
  472. <el-row :gutter="20">
  473. <el-col :span="12">
  474. <el-form-item label="税率:" prop="taxRate">
  475. <el-select v-model="contractForm.taxRate" placeholder="请选择" style="width: 100%;">
  476. <el-option
  477. v-for="item in taxRateList"
  478. :key="item.id"
  479. :label="item.taxrateName"
  480. :value="item.id"
  481. />
  482. </el-select>
  483. </el-form-item>
  484. </el-col>
  485. <el-col :span="12">
  486. <el-form-item label="结算方式:" prop="settlementMethod">
  487. <el-select v-model="contractForm.settlementMethod" placeholder="请选择" style="width: 100%;">
  488. <el-option
  489. v-for="item in settlementMethodList"
  490. :key="item.id"
  491. :label="item.settlementName"
  492. :value="item.id"
  493. />
  494. </el-select>
  495. </el-form-item>
  496. </el-col>
  497. </el-row>
  498. <el-row :gutter="20">
  499. <el-col :span="24">
  500. <el-form-item label="合同附件:" prop="contractAttachment">
  501. <FileUpload
  502. v-model="contractForm.contractAttachment"
  503. :limit="10"
  504. :file-size="50"
  505. :file-type="['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'pdf']"
  506. :disabled="contractDialogReadonly"
  507. />
  508. </el-form-item>
  509. </el-col>
  510. </el-row>
  511. <el-row :gutter="20">
  512. <el-col :span="24">
  513. <el-form-item label="合同说明:" prop="contractDescription">
  514. <el-input
  515. v-model="contractForm.contractDescription"
  516. type="textarea"
  517. :rows="4"
  518. placeholder="请输入合同说明"
  519. />
  520. </el-form-item>
  521. </el-col>
  522. </el-row>
  523. </el-form>
  524. <template #footer>
  525. <div class="dialog-footer">
  526. <el-button @click="contractDialogVisible = false">取消</el-button>
  527. <el-button v-if="!contractDialogReadonly" type="primary" @click="handleContractSubmit" :loading="contractSubmitLoading">确定</el-button>
  528. </div>
  529. </template>
  530. </el-dialog>
  531. <!-- 品牌新增对话框 -->
  532. <el-dialog
  533. v-model="brandDialogVisible"
  534. title="添加品牌"
  535. width="600px"
  536. :close-on-click-modal="false"
  537. class="brand-dialog"
  538. >
  539. <div class="brand-dialog__search">
  540. <el-form @submit.prevent>
  541. <el-row :gutter="12" align="middle">
  542. <el-col :span="14">
  543. <el-form-item label="品牌名称:" style="margin-bottom: 0;">
  544. <el-input
  545. v-model="brandSearchKeyword"
  546. placeholder="请输入品牌名称"
  547. @keyup.enter="handleSearchBrand"
  548. />
  549. </el-form-item>
  550. </el-col>
  551. <el-col :span="10" style="display: flex; justify-content: flex-end;">
  552. <el-form-item label-width="0" style="margin-bottom: 0;">
  553. <el-button type="primary" @click="handleSearchBrand" :loading="brandSearchLoading">搜索</el-button>
  554. <el-button type="primary" @click="handleAddBrandToList()">添加</el-button>
  555. </el-form-item>
  556. </el-col>
  557. </el-row>
  558. </el-form>
  559. </div>
  560. <!-- 搜索结果列表 -->
  561. <div v-if="brandSearchResults.length > 0" class="brand-search-results">
  562. <div class="search-results-title">搜索结果(点击添加):</div>
  563. <div class="brand-result-item"
  564. v-for="brand in brandSearchResults"
  565. :key="brand.id"
  566. @click="handleAddBrandToList(brand)"
  567. style="cursor: pointer;"
  568. >
  569. <span class="brand-result-name">{{ brand.brandName }}</span>
  570. <span class="brand-result-no">编号: {{ brand.brandNo }}</span>
  571. </div>
  572. </div>
  573. <div class="selected-brands-section">
  574. <div class="section-label">已选择品牌:</div>
  575. <div class="brand-tags-container">
  576. <el-tag
  577. v-for="brand in tempSelectedBrands"
  578. :key="brand.id"
  579. closable
  580. @close="handleRemoveTempBrand(brand)"
  581. type="info"
  582. style="margin-right: 10px; margin-bottom: 10px;"
  583. >
  584. {{ brand.brandName }}
  585. </el-tag>
  586. <span v-if="tempSelectedBrands.length === 0" style="color: #999; font-size: 14px;">暂无已选择品牌</span>
  587. </div>
  588. </div>
  589. <template #footer>
  590. <div class="dialog-footer">
  591. <el-button @click="handleBrandDialogClose">取消</el-button>
  592. <el-button type="primary" @click="handleBrandSubmit" :loading="brandSubmitLoading">保存</el-button>
  593. </div>
  594. </template>
  595. </el-dialog>
  596. </div>
  597. </template>
  598. <script setup lang="ts">
  599. import { ref, onMounted } from 'vue';
  600. import { useRoute, useRouter } from 'vue-router';
  601. import { ArrowLeft, Plus, Search } from '@element-plus/icons-vue';
  602. import { ElMessage, ElMessageBox } from 'element-plus';
  603. import { useUserStore } from '@/store/modules/user';
  604. import RegionCascader from '@/components/RegionCascader/index.vue';
  605. import FileUpload from '@/components/FileUpload/index.vue';
  606. import Pagination from '@/components/Pagination/index.vue';
  607. import { getInfo, addInfo, updateInfo, scmEditInfo, getStaffListSplice, getSupplierStaffIds, getContactListById, getSupplierCategories, getSupplierContractsById, getBankBySupplierId, getAuthorizeDetailList, savePurchaseInfo, getDictData, getTaxRateList, getSettlementMethodList, getInvoiceTypeList, listInfo } from '@/api/customer/info';
  608. import { getBank, updateBank, addBank } from '@/api/customer/bank';
  609. import { BankForm } from '@/api/customer/bank/types';
  610. import { getContact, addContact, updateContact } from '@/api/customer/contact';
  611. import { ContactForm } from '@/api/customer/contact/types';
  612. import { InfoVO } from '@/api/customer/info/types';
  613. import { listCompany } from '@/api/company/company';
  614. import { CompanyVO } from '@/api/company/company/types';
  615. import { listEnterpriseScale } from '@/api/customer/customerCategory/enterpriseScale';
  616. import { EnterpriseScaleVO } from '@/api/customer/customerCategory/enterpriseScale/types';
  617. import { listIndustryCategory } from '@/api/customer/customerCategory/industryCategory';
  618. import { IndustryCategoryVO } from '@/api/customer/customerCategory/industryCategory/types';
  619. import { listLevel } from '@/api/system/level';
  620. import { LevelVO } from '@/api/system/level/types';
  621. import { listType } from '@/api/system/type';
  622. import { TypeVO } from '@/api/system/type/types';
  623. import { listBrand, getBrand } from '@/api/product/brand';
  624. import { BrandVO } from '@/api/product/brand/types';
  625. import { listAddress, getAddress, addAddress, updateAddress, delAddress } from '@/api/supplier/address';
  626. import { AddressForm } from '@/api/supplier/address/types';
  627. import { addArea, listArea, getArea } from '@/api/supplier/area';
  628. import { listContract, getContract, addContract, updateContract } from '@/api/supplier/contract';
  629. import { ContractForm } from '@/api/supplier/contract/types';
  630. import { getBusinessInformation } from '@/api/customer/bussineInfo/index';
  631. import { listInvoiceType } from '@/api/system/invoiceType';
  632. import { InvoiceTypeVO } from '@/api/system/invoiceType/types';
  633. import { listBank as listSystemBank } from '@/api/system/bank';
  634. import { BankVO as SystemBankVO } from '@/api/system/bank/types';
  635. import { getInfoTemporary } from '@/api/supplier/infoTemporary';
  636. import { getChinaArea } from '@/api/system/addressarea/index';
  637. import download from '@/plugins/download';
  638. import BasicInfoTab from './components/BasicInfoTab.vue';
  639. import PurchaseInfoTab from './components/PurchaseInfoTab.vue';
  640. import ContactTab from './components/ContactTab.vue';
  641. import SupplyInfoTab from './components/SupplyInfoTab.vue';
  642. import AddressTab from './components/AddressTab.vue';
  643. import ContractTab from './components/ContractTab.vue';
  644. const route = useRoute();
  645. const router = useRouter();
  646. const userStore = useUserStore();
  647. const activeTab = ref('basic');
  648. const isAddMode = ref(false); // 是否为新增模式
  649. const isEditMode = ref(false); // 是否为编辑模式
  650. const isViewMode = ref(false); // 是否为查看模式
  651. const isBasicInfoSaved = ref(false); // 基础信息是否已保存(仅用于新增模式)
  652. const detailData = ref<InfoVO>({} as InfoVO);
  653. const staffOptions = ref<any[]>([]);
  654. const selectedProductManager = ref<number | null>(null);
  655. const selectedBuyer = ref<number | null>(null);
  656. const companyOptions = ref<CompanyVO[]>([]);
  657. const enterpriseScaleOptions = ref<EnterpriseScaleVO[]>([]);
  658. const industryCategoryOptions = ref<IndustryCategoryVO[]>([]);
  659. const supplierLevelOptions = ref<LevelVO[]>([]);
  660. const supplierTypeOptions = ref<TypeVO[]>([]);
  661. // 工商信息对象
  662. const businessInfo = ref<any>({});
  663. const businessInfoLoading = ref(false); // 获取工商信息的loading状态
  664. const businessInfoCache = new Map<string, any>();
  665. const lastBusinessInfoQuery = ref('');
  666. const lastBusinessInfoQueryAt = ref(0);
  667. const businessInfoInFlight = ref(false);
  668. // 联系人相关数据
  669. const contactList = ref<any[]>([]);
  670. const contactLoading = ref(false);
  671. const contactSearchParams = ref({
  672. userNo: '',
  673. userName: ''
  674. });
  675. // 联系人对话框相关
  676. const contactDialogVisible = ref(false);
  677. const contactDialogTitle = ref('');
  678. const contactDialogReadonly = ref(false);
  679. const contactFormRef = ref<ElFormInstance>();
  680. const contactSubmitLoading = ref(false);
  681. const contactForm = ref<ContactForm>({
  682. supplierNo: '',
  683. supplierId: undefined,
  684. userNo: '',
  685. userName: '',
  686. phone: '',
  687. roleNo: '',
  688. departmentNo: '',
  689. position: '',
  690. isPrimaryContact: '0',
  691. isRegister: '0',
  692. email: '',
  693. fax: '',
  694. remark: ''
  695. });
  696. const onContactPhoneInput = (val: string) => {
  697. const next = (val || '').replace(/\D+/g, '').slice(0, 11);
  698. if (next !== contactForm.value.phone) {
  699. contactForm.value.phone = next;
  700. }
  701. };
  702. // 联系人表单验证规则
  703. const contactFormRules = {
  704. userName: [{ required: true, message: '请输入员工姓名', trigger: 'blur' }],
  705. phone: [
  706. { required: true, message: '请输入手机号', trigger: 'blur' },
  707. { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的11位手机号', trigger: 'blur' }
  708. ]
  709. };
  710. // 供应信息相关数据
  711. const productCategoryList = ref<any[]>([]);
  712. const selectedCategories = ref<string[]>([]);
  713. const supplyAreaList = ref<any[]>([]);
  714. // 授权详情信息列表(静态数据)
  715. const authorizationList = ref<any[]>([]);
  716. const authorizationPagination = ref({
  717. pageNum: 1,
  718. pageSize: 10,
  719. total: 0
  720. });
  721. // 供货区域对话框相关
  722. const supplyAreaDialogVisible = ref(false);
  723. const supplyAreaSubmitLoading = ref(false);
  724. const selectedSupplyAreaCodes = ref<string[]>([]); // 选中的供货区域(省/市ID数组)
  725. const savedAreaData = ref<any[]>([]); // 已保存的供货区域数据
  726. // 供货区域选择器数据源(从接口获取)
  727. const supplyAreaOptions = ref<any[]>([]);
  728. // 详细地址级联选择器相关
  729. const regionOptions = ref<any[]>([]); // 省市区级联选择器选项
  730. const selectedOfficeRegion = ref<string[]>([]); // 选中的省市区代码数组
  731. // 品牌相关数据
  732. const selectedBrands = ref<BrandVO[]>([]); // 已选择的品牌列表
  733. const brandDialogVisible = ref(false);
  734. const brandSearchKeyword = ref(''); // 品牌搜索关键词
  735. const tempSelectedBrands = ref<BrandVO[]>([]); // 弹框中临时选择的品牌
  736. const brandSearchResults = ref<BrandVO[]>([]); // 品牌搜索结果
  737. const allBrandList = ref<BrandVO[]>([]); // 所有品牌列表
  738. const brandSubmitLoading = ref(false);
  739. const brandSearchLoading = ref(false);
  740. /** 获取授权详情列表 */
  741. const getAuthorizationList = async () => {
  742. let id = route.query.id as string;
  743. // 如果URL中没有id,暂时跳过
  744. if (!id) {
  745. console.warn('缺少供应商ID,无法获取授权详情列表');
  746. return;
  747. }
  748. try {
  749. const res = await getAuthorizeDetailList({
  750. supplierId: id,
  751. pageNum: authorizationPagination.value.pageNum,
  752. pageSize: authorizationPagination.value.pageSize
  753. });
  754. console.log('授权详情API返回:', res);
  755. authorizationList.value = res.rows || res.data || [];
  756. authorizationPagination.value.total = res.total || 0;
  757. console.log('授权详情列表:', authorizationList.value);
  758. console.log('分页信息:', authorizationPagination.value);
  759. } catch (e) {
  760. console.error('获取授权详情列表失败:', e);
  761. }
  762. };
  763. // 地址管理列表
  764. const addressList = ref<any[]>([]);
  765. // 地址管理对话框相关
  766. const addressDialogVisible = ref(false);
  767. const addressDialogTitle = ref('');
  768. const addressDialogReadonly = ref(false);
  769. const addressFormRef = ref<ElFormInstance>();
  770. const addressSubmitLoading = ref(false);
  771. const selectedAddressRegion = ref<string[]>([]); // 地址的省市区代码数组
  772. const addressForm = ref<AddressForm>({
  773. supplierNo: '',
  774. supplierId: undefined,
  775. shipperName: '',
  776. shipperPhone: '',
  777. shippingPostCode: '',
  778. shippingProvincial: '',
  779. shippingCity: '',
  780. shippingCounty: '',
  781. shippingAddress: '',
  782. isSelf: 0
  783. });
  784. // 地址表单验证规则
  785. const addressFormRules = {
  786. shipperName: [{ required: true, message: '请输入收货人', trigger: 'blur' }],
  787. shipperPhone: [{ required: true, message: '请输入手机号码', trigger: 'blur' }],
  788. shippingProvincial: [{ required: true, message: '请选择地址', trigger: 'change' }],
  789. shippingAddress: [{ required: true, message: '请输入详细地址', trigger: 'blur' }]
  790. };
  791. // 付款信息列表
  792. const paymentInfoList = ref<any[]>([]);
  793. // 付款信息对话框相关
  794. const paymentDialogVisible = ref(false);
  795. const paymentDialogTitle = ref('');
  796. const paymentDialogReadonly = ref(false);
  797. const paymentFormRef = ref<ElFormInstance>();
  798. const paymentSubmitLoading = ref(false);
  799. const paymentForm = ref<BankForm>({
  800. num: undefined,
  801. supplierNo: '',
  802. bankNum: '',
  803. bankInfoNo: undefined,
  804. bankName: '',
  805. bankNo: '',
  806. isture: '1',
  807. circlesName: '',
  808. phone: '',
  809. invoiceTypeNo: undefined,
  810. invoiceTypeName: '',
  811. businessName: '',
  812. businessAddress: ''
  813. });
  814. // 发票类型列表和银行列表
  815. const invoiceTypeList = ref<InvoiceTypeVO[]>([]);
  816. const systemBankList = ref<SystemBankVO[]>([]);
  817. // 付款信息表单验证规则
  818. const paymentFormRules = {
  819. invoiceTypeNo: [{ required: true, message: '请选择开票类型', trigger: 'change' }],
  820. bankNum: [{ required: true, message: '请输入开户行行号', trigger: 'blur' }],
  821. bankInfoNo: [{ required: true, message: '请选择开户行名称', trigger: 'change' }],
  822. bankNo: [{ required: true, message: '请输入银行账户', trigger: 'blur' }],
  823. phone: [{ required: true, message: '请输入固定电话', trigger: 'blur' }],
  824. num: [{ required: true, message: '请选择是否主账号', trigger: 'change' }]
  825. };
  826. /** 获取发票类型列表 */
  827. const getInvoiceTypeData = async () => {
  828. try {
  829. const res = await listInvoiceType({ pageNum: 1, pageSize: 1000 });
  830. invoiceTypeList.value = res.rows || [];
  831. } catch (e) {
  832. console.error('获取发票类型失败:', e);
  833. }
  834. };
  835. /** 获取银行列表 */
  836. const getSystemBankData = async () => {
  837. try {
  838. const res = await listSystemBank({ pageNum: 1, pageSize: 1000 });
  839. systemBankList.value = res.rows || [];
  840. } catch (e) {
  841. console.error('获取银行列表失败:', e);
  842. }
  843. };
  844. /** 获取付款信息 */
  845. const getPaymentInfo = async () => {
  846. const id = route.query.id as string;
  847. if (!id) return;
  848. try {
  849. const res = await getBankBySupplierId(id);
  850. // 后端返回数组
  851. if (res.data) {
  852. paymentInfoList.value = Array.isArray(res.data) ? res.data : [res.data];
  853. }
  854. } catch (e) {
  855. console.error('获取付款信息失败:', e);
  856. }
  857. };
  858. // 合同管理相关数据
  859. const contractList = ref<any[]>([]);
  860. const contractSearchParams = ref({
  861. contractNo: '',
  862. contractName: '',
  863. contractType: '',
  864. contractStartTime: '' as string | Date | '',
  865. contractEndTime: '' as string | Date | '',
  866. contractStatus: ''
  867. });
  868. const contractPagination = ref({
  869. pageNum: 1,
  870. pageSize: 10,
  871. total: 0
  872. });
  873. const contractStatusDict = ref<any[]>([
  874. { dictLabel: '待审核', dictValue: '0' },
  875. { dictLabel: '生效', dictValue: '1' },
  876. { dictLabel: '失效', dictValue: '2' }
  877. ]);
  878. // 合同对话框相关
  879. const contractDialogVisible = ref(false);
  880. const contractDialogTitle = ref('');
  881. const contractDialogReadonly = ref(false);
  882. const contractFormRef = ref<ElFormInstance>();
  883. const contractSubmitLoading = ref(false);
  884. const contractTypeDict = ref<any[]>([]); // 合同类型字典
  885. const taxRateList = ref<any[]>([]); // 税率列表
  886. const settlementMethodList = ref<any[]>([]); // 结算方式列表
  887. const contractForm = ref<ContractForm>({
  888. supplierNo: '',
  889. supplierId: undefined,
  890. contractNo: '',
  891. contractName: '',
  892. contractType: '',
  893. contractStartTime: '',
  894. contractEndTime: '',
  895. contractStatus: 0,
  896. demandReminderTime: 4,
  897. taxRate: undefined,
  898. contractAmount: undefined,
  899. contractDescription: '',
  900. contractAttachment: '',
  901. settlementMethod: '',
  902. invoiceType: ''
  903. });
  904. // 合同表单验证规则
  905. const contractFormRules = {
  906. contractName: [{ required: true, message: '请输入合同名称', trigger: 'blur' }],
  907. contractType: [{ required: true, message: '请选择合同类型', trigger: 'change' }],
  908. contractStartTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
  909. contractEndTime: [{ required: true, message: '请选择截止时间', trigger: 'change' }]
  910. };
  911. /** 返回列表页 */
  912. const goBack = () => {
  913. // 使用浏览器后退,返回上一页
  914. router.back();
  915. };
  916. /** 获取工商信息 */
  917. const handleGetBusinessInfo = async () => {
  918. const queryName = (detailData.value.businessName || '').trim();
  919. if (!queryName) {
  920. ElMessage.warning('请先输入工商名称');
  921. return;
  922. }
  923. const now = Date.now();
  924. if (businessInfoInFlight.value && lastBusinessInfoQuery.value === queryName) {
  925. return;
  926. }
  927. if (lastBusinessInfoQuery.value === queryName && now - lastBusinessInfoQueryAt.value < 1500) {
  928. return;
  929. }
  930. if (businessInfoCache.has(queryName)) {
  931. const cached = businessInfoCache.get(queryName);
  932. businessInfo.value = cached;
  933. if (cached?.businessName) detailData.value.businessName = cached.businessName;
  934. if (cached?.socialCreditCode) detailData.value.socialCreditCode = cached.socialCreditCode;
  935. if (cached?.legalPersonName) detailData.value.legalPersonName = cached.legalPersonName;
  936. if (cached?.registeredCapital) detailData.value.registeredCapital = cached.registeredCapital;
  937. return;
  938. }
  939. try {
  940. businessInfoLoading.value = true;
  941. businessInfoInFlight.value = true;
  942. lastBusinessInfoQuery.value = queryName;
  943. lastBusinessInfoQueryAt.value = now;
  944. // 调用获取工商信息接口
  945. const res = await getBusinessInformation(queryName);
  946. if (res.code === 200 && res.data) {
  947. // 将获取到的工商信息填充到表单
  948. businessInfo.value = res.data;
  949. businessInfoCache.set(queryName, res.data);
  950. // 自动填充基本信息表单的相关字段
  951. if (res.data.businessName) {
  952. detailData.value.businessName = res.data.businessName;
  953. }
  954. if (res.data.socialCreditCode) {
  955. detailData.value.socialCreditCode = res.data.socialCreditCode;
  956. }
  957. if (res.data.legalPersonName) {
  958. detailData.value.legalPersonName = res.data.legalPersonName;
  959. }
  960. if (res.data.registeredCapital) {
  961. detailData.value.registeredCapital = res.data.registeredCapital;
  962. }
  963. ElMessage.success('工商信息获取成功');
  964. } else {
  965. ElMessage.error(res.msg || '获取工商信息失败');
  966. }
  967. } catch (e) {
  968. console.error('获取工商信息失败:', e);
  969. ElMessage.error('获取工商信息失败');
  970. } finally {
  971. businessInfoLoading.value = false;
  972. businessInfoInFlight.value = false;
  973. }
  974. };
  975. /** 保存数据 */
  976. const handleSave = async () => {
  977. try {
  978. // 验证必填字段
  979. if (!detailData.value.ownedCompany) {
  980. ElMessage.warning('请选择所属公司');
  981. return;
  982. }
  983. if (!detailData.value.enterpriseName) {
  984. ElMessage.warning('请输入企业名称');
  985. return;
  986. }
  987. if (!detailData.value.businessName) {
  988. ElMessage.warning('请输入工商名称');
  989. return;
  990. }
  991. if (detailData.value.fixedPhone && !/^\d+$/.test(String(detailData.value.fixedPhone))) {
  992. ElMessage.warning('固定电话只能输入数字');
  993. return;
  994. }
  995. if (!detailData.value.shortName) {
  996. ElMessage.warning('请输入企业简称');
  997. return;
  998. }
  999. if (!detailData.value.cooperateLevel) {
  1000. ElMessage.warning('请选择供应商等级');
  1001. return;
  1002. }
  1003. if (!detailData.value.membershipSize) {
  1004. ElMessage.warning('请选择企业规模');
  1005. return;
  1006. }
  1007. if (!detailData.value.industrCategory) {
  1008. ElMessage.warning('请选择行业类别');
  1009. return;
  1010. }
  1011. if (!detailData.value.supplierType) {
  1012. ElMessage.warning('请选择供应商类型');
  1013. return;
  1014. }
  1015. if (!detailData.value.officeProvince) {
  1016. ElMessage.warning('请选择详细地址省份');
  1017. return;
  1018. }
  1019. // 准备提交数据,包含工商信息
  1020. const submitData: any = {
  1021. ...detailData.value,
  1022. supplierType: String(detailData.value.supplierType), // 确保是字符串类型
  1023. cooperateLevel: String(detailData.value.cooperateLevel), // 确保是字符串类型
  1024. // 将工商信息对象转换为 JSON 字符串存储到 otherCustomers 字段
  1025. otherCustomers: businessInfo.value && Object.keys(businessInfo.value).length > 0
  1026. ? JSON.stringify(businessInfo.value)
  1027. : detailData.value.otherCustomers
  1028. };
  1029. // 根据模式调用新增或更新接口
  1030. let res;
  1031. if (isAddMode.value && !isBasicInfoSaved.value) {
  1032. // 新增模式,调用新增接口
  1033. res = await addInfo(submitData);
  1034. } else {
  1035. // 编辑模式,调用SCM更新接口(不走审核)
  1036. res = await scmEditInfo(submitData);
  1037. }
  1038. ElMessage.success('保存成功');
  1039. // 如果是新增模式,保存成功后标记为已保存,启用其他标签页
  1040. if (isAddMode.value && !isBasicInfoSaved.value) {
  1041. // 获取新创建的供应商ID
  1042. let newId = null;
  1043. if (res.data && res.data.id) {
  1044. newId = res.data.id;
  1045. } else {
  1046. // 如果后端没返回ID,通过企业名称查询获取ID
  1047. try {
  1048. const listRes = await listInfo({
  1049. enterpriseName: detailData.value.enterpriseName,
  1050. pageNum: 1,
  1051. pageSize: 1
  1052. });
  1053. if (listRes.rows && listRes.rows.length > 0) {
  1054. newId = listRes.rows[0].id;
  1055. }
  1056. } catch (e) {
  1057. console.error('获取新创建的供应商ID失败:', e);
  1058. }
  1059. }
  1060. // 更新路由参数(保持 mode=add,添加 id)
  1061. if (newId) {
  1062. await router.replace({
  1063. path: route.path,
  1064. query: { id: newId, mode: 'add' }
  1065. });
  1066. // 标记基础信息已保存,启用其他标签页
  1067. isBasicInfoSaved.value = true;
  1068. detailData.value.id = newId;
  1069. // 重新加载供应商详情以获取完整信息(包括supplierNo)
  1070. try {
  1071. const detailRes = await getInfo(newId);
  1072. detailData.value = detailRes.data;
  1073. } catch (e) {
  1074. console.error('加载供应商详情失败:', e);
  1075. }
  1076. // 加载其他标签页数据
  1077. getContactList();
  1078. getProductCategories();
  1079. getContractList();
  1080. getPaymentInfo();
  1081. getAuthorizationList();
  1082. getAddressList();
  1083. getSupplyAreaList();
  1084. } else {
  1085. ElMessage.warning('无法获取新创建的供应商ID');
  1086. }
  1087. } else {
  1088. // 编辑模式,直接刷新当前数据
  1089. if (detailData.value.id) {
  1090. await getDetail();
  1091. }
  1092. }
  1093. } catch (e) {
  1094. console.error('保存失败:', e);
  1095. ElMessage.error('保存失败');
  1096. }
  1097. };
  1098. /** 初始化省市区级联选择器数据 */
  1099. const initRegionOptions = async () => {
  1100. try {
  1101. const { regionData } = await import('element-china-area-data');
  1102. regionOptions.value = regionData as any[];
  1103. } catch (e) {
  1104. console.error('初始化省市区数据失败:', e);
  1105. }
  1106. };
  1107. /** 初始化供货区域选项数据 */
  1108. const initSupplyAreaOptions = async () => {
  1109. try {
  1110. const res = await getChinaArea();
  1111. const raw = (res as any)?.data || (res as any)?.rows || res || [];
  1112. const mapTree = (nodes: any[]): any[] => {
  1113. return (nodes || []).map((n: any) => ({
  1114. label: n.areaName,
  1115. value: String(n.id),
  1116. areaCode: String(n.areaCode ?? ''),
  1117. parentId: n.parentCode ?? n.parentId,
  1118. level: n.level,
  1119. children: n.children && n.children.length ? mapTree(n.children) : []
  1120. }));
  1121. };
  1122. supplyAreaOptions.value = mapTree(raw);
  1123. } catch (e) {
  1124. console.error('初始化供货区域选项失败:', e);
  1125. supplyAreaOptions.value = [];
  1126. }
  1127. };
  1128. /** 详细地址省市区改变事件 */
  1129. const handleOfficeRegionChange = (value: string[]) => {
  1130. if (value && value.length === 3) {
  1131. // value 是 [省代码, 市代码, 区代码]
  1132. const [provinceCode, cityCode, districtCode] = value;
  1133. // 同时保存 code(后端若支持可直接入库,不支持则忽略;前端回显优先使用 code)
  1134. (detailData.value as any).officeProvinceCode = provinceCode;
  1135. (detailData.value as any).officeCityCode = cityCode;
  1136. (detailData.value as any).officeCountyCode = districtCode;
  1137. // 从 regionOptions 中查找对应的名称
  1138. let provinceName = '';
  1139. let cityName = '';
  1140. let districtName = '';
  1141. const province = regionOptions.value.find((p: any) => p.value === provinceCode);
  1142. if (province) {
  1143. provinceName = province.label;
  1144. const city = province.children?.find((c: any) => c.value === cityCode);
  1145. if (city) {
  1146. cityName = city.label;
  1147. const district = city.children?.find((d: any) => d.value === districtCode);
  1148. if (district) {
  1149. districtName = district.label;
  1150. }
  1151. }
  1152. }
  1153. // 更新 detailData
  1154. detailData.value.officeProvince = provinceName;
  1155. detailData.value.officeCity = cityName;
  1156. detailData.value.officeCounty = districtName;
  1157. } else {
  1158. // 清空
  1159. (detailData.value as any).officeProvinceCode = '';
  1160. (detailData.value as any).officeCityCode = '';
  1161. (detailData.value as any).officeCountyCode = '';
  1162. detailData.value.officeProvince = '';
  1163. detailData.value.officeCity = '';
  1164. detailData.value.officeCounty = '';
  1165. }
  1166. };
  1167. /** 获取详情数据 */
  1168. const getDetail = async () => {
  1169. const id = route.query.id as string;
  1170. const mode = route.query.mode as string;
  1171. // 重置所有模式标志
  1172. isAddMode.value = false;
  1173. isEditMode.value = false;
  1174. isViewMode.value = false;
  1175. isBasicInfoSaved.value = false;
  1176. // 判断模式
  1177. if (mode === 'add') {
  1178. // 新增模式
  1179. isAddMode.value = true;
  1180. if (id) {
  1181. // 如果有ID,说明基础信息已保存
  1182. isBasicInfoSaved.value = true;
  1183. }
  1184. return;
  1185. } else if (mode === 'edit' && id) {
  1186. // 编辑模式(必须有ID)
  1187. isEditMode.value = true;
  1188. } else if (id) {
  1189. // 查看模式(有ID但没有mode或mode=view)
  1190. isViewMode.value = true;
  1191. } else {
  1192. // 没有ID也没有mode,默认为新增模式
  1193. isAddMode.value = true;
  1194. return;
  1195. }
  1196. // 有ID的情况下,加载详情数据
  1197. if (id) {
  1198. const res = await getInfo(id);
  1199. detailData.value = res.data;
  1200. // 如果是edit模式且供应商状态为"待修改审核"(4),需要查询临时表数据
  1201. if (isEditMode.value && detailData.value.supplyStatus === '4') {
  1202. try {
  1203. const tempRes = await getInfoTemporary(id);
  1204. if (tempRes.data) {
  1205. // 使用临时表数据覆盖主表数据
  1206. const tempData: any = tempRes.data;
  1207. detailData.value = {
  1208. ...detailData.value, // 保留主表的一些基础字段
  1209. ...tempData, // 使用临时表的修改数据
  1210. id: detailData.value.id, // 确保ID是主表的ID
  1211. supplierNo: detailData.value.supplierNo // 确保supplierNo是主表的
  1212. } as any;
  1213. console.log('已加载临时表数据:', tempData);
  1214. }
  1215. } catch (e) {
  1216. console.error('查询临时表失败:', e);
  1217. // 查询失败,继续使用主表数据
  1218. }
  1219. }
  1220. // 如果 personImageUrl 为空但 personImage 有值,使用 personImage 的值
  1221. if (!detailData.value.personImageUrl && detailData.value.personImage) {
  1222. detailData.value.personImageUrl = detailData.value.personImage;
  1223. }
  1224. // 初始化品牌列表
  1225. initBrandList();
  1226. // 供货区域列表会在 onMounted 中通过 getSupplyAreaList() 获取,这里不需要初始化
  1227. // 注释掉简单的初始化,避免与 getSupplyAreaList 冲突
  1228. // if ((detailData.value as any).province || (detailData.value as any).city) {
  1229. // supplyAreaList.value = [{
  1230. // province: (detailData.value as any).province || '',
  1231. // city: (detailData.value as any).city || ''
  1232. // }];
  1233. // }
  1234. // 初始化详细地址显示:优先使用已保存的 code;没有 code 时再用 name 反查 code
  1235. const officeProvinceCode = (detailData.value as any).officeProvinceCode;
  1236. const officeCityCode = (detailData.value as any).officeCityCode;
  1237. const officeCountyCode = (detailData.value as any).officeCountyCode;
  1238. if (officeProvinceCode && officeCityCode && officeCountyCode) {
  1239. selectedOfficeRegion.value = [String(officeProvinceCode), String(officeCityCode), String(officeCountyCode)];
  1240. } else if (detailData.value.officeProvince && detailData.value.officeCity && detailData.value.officeCounty) {
  1241. const province = detailData.value.officeProvince;
  1242. const city = detailData.value.officeCity;
  1243. const county = detailData.value.officeCounty;
  1244. // 根据省市区名称查找对应的代码
  1245. const provinceItem = regionOptions.value.find((p: any) => p.label === province);
  1246. if (provinceItem) {
  1247. const cityItem = provinceItem.children?.find((c: any) => c.label === city);
  1248. if (cityItem) {
  1249. const districtItem = cityItem.children?.find((d: any) => d.label === county);
  1250. if (districtItem) {
  1251. selectedOfficeRegion.value = [provinceItem.value, cityItem.value, districtItem.value];
  1252. }
  1253. }
  1254. }
  1255. }
  1256. // 解析工商信息,支持 otherCustomersMap 或 otherCustomers
  1257. const otherCustomersData = (detailData.value as any).otherCustomersMap || detailData.value.otherCustomers;
  1258. if (otherCustomersData) {
  1259. try {
  1260. // 如果是字符串,需要解析JSON
  1261. if (typeof otherCustomersData === 'string') {
  1262. businessInfo.value = JSON.parse(otherCustomersData);
  1263. } else {
  1264. businessInfo.value = otherCustomersData;
  1265. }
  1266. console.log('工商信息:', businessInfo.value);
  1267. } catch (e) {
  1268. console.error('解析工商信息失败:', e);
  1269. }
  1270. }
  1271. // 工商信息字段已经在 detailData 中返回,直接使用
  1272. // 将 detailData 的工商信息字段映射到 businessInfo
  1273. businessInfo.value = {
  1274. businessName: detailData.value.businessName,
  1275. registrationAuthority: (detailData.value as any).registrationAuthority,
  1276. establishmentDate: (detailData.value as any).establishmentDate,
  1277. registrationStatus: (detailData.value as any).registrationStatus,
  1278. paidInCapital: (detailData.value as any).paidInCapital,
  1279. socialCreditCode: detailData.value.socialCreditCode,
  1280. legalPersonName: detailData.value.legalPersonName,
  1281. registeredCapital: detailData.value.registeredCapital,
  1282. revocationDate: (detailData.value as any).revocationDate,
  1283. bussinessRange: (detailData.value as any).bussinessRange,
  1284. businessAddress: (detailData.value as any).businessAddress
  1285. };
  1286. console.log('工商信息(从详情接口):', businessInfo.value);
  1287. // 获取当前供应商的产品经理和采购员ID
  1288. try {
  1289. const staffIdsRes = await getSupplierStaffIds(id);
  1290. const staffIds = staffIdsRes.data; // {productManager: 1, purchaser: 2}
  1291. selectedProductManager.value = staffIds.productManager;
  1292. selectedBuyer.value = staffIds.purchaser;
  1293. console.log('当前产品经理ID:', staffIds.productManager);
  1294. console.log('当前采购员ID:', staffIds.purchaser);
  1295. } catch (e) {
  1296. console.error('获取人员ID失败:', e);
  1297. }
  1298. }
  1299. };
  1300. /** 获取人员下拉选项 */
  1301. const getStaffOptions = async () => {
  1302. try {
  1303. const res = await getStaffListSplice();
  1304. const staffMap = res.data; // Map<Long, String> 格式:{1: "00040,郑春风", 2: "00050,王坤"}
  1305. // 转换为下拉框选项格式
  1306. staffOptions.value = Object.entries(staffMap).map(([staffId, displayText]) => ({
  1307. staffId: Number(staffId),
  1308. displayText: displayText,
  1309. label: displayText,
  1310. value: Number(staffId)
  1311. }));
  1312. } catch (e) {
  1313. console.error('获取人员信息失败:', e);
  1314. }
  1315. };
  1316. /** 获取公司下拉选项 */
  1317. const getCompanyOptions = async () => {
  1318. try {
  1319. const res = await listCompany({
  1320. pageNum: 1,
  1321. pageSize: 1000,
  1322. status: '0'
  1323. }); // 只获取正常状态的公司
  1324. companyOptions.value = res.rows || [];
  1325. } catch (e) {
  1326. console.error('获取公司信息失败:', e);
  1327. }
  1328. };
  1329. /** 获取企业规模下拉选项 */
  1330. const getEnterpriseScaleOptions = async () => {
  1331. try {
  1332. const res = await listEnterpriseScale({
  1333. pageNum: 1,
  1334. pageSize: 1000,
  1335. status: '0' // 只获取正常状态的企业规模
  1336. });
  1337. enterpriseScaleOptions.value = res.data || res.rows || [];
  1338. } catch (e) {
  1339. console.error('获取企业规模信息失败:', e);
  1340. }
  1341. };
  1342. /** 获取行业类别下拉选项 */
  1343. const getIndustryCategoryOptions = async () => {
  1344. try {
  1345. const res = await listIndustryCategory({
  1346. pageNum: 1,
  1347. pageSize: 1000,
  1348. status: '0' // 只获取正常状态的行业类别
  1349. });
  1350. industryCategoryOptions.value = res.data || res.rows || [];
  1351. } catch (e) {
  1352. console.error('获取行业类别信息失败:', e);
  1353. }
  1354. };
  1355. /** 获取供应商等级下拉选项 */
  1356. const getSupplierLevelOptions = async () => {
  1357. try {
  1358. const res = await listLevel({
  1359. pageNum: 1,
  1360. pageSize: 1000
  1361. });
  1362. supplierLevelOptions.value = res.data || res.rows || [];
  1363. } catch (e) {
  1364. console.error('获取供应商等级信息失败:', e);
  1365. }
  1366. };
  1367. /** 获取供应商类型下拉选项 */
  1368. const getSupplierTypeOptions = async () => {
  1369. try {
  1370. const res = await listType({
  1371. pageNum: 1,
  1372. pageSize: 1000
  1373. } as any);
  1374. supplierTypeOptions.value = res.data || res.rows || [];
  1375. } catch (e) {
  1376. console.error('获取供应商类型信息失败:', e);
  1377. }
  1378. };
  1379. /** 获取联系人列表 */
  1380. const getContactList = async () => {
  1381. const id = route.query.id as string;
  1382. if (!id) return;
  1383. contactLoading.value = true;
  1384. try {
  1385. const res = await getContactListById(id, {
  1386. pageNum: 1,
  1387. pageSize: 100,
  1388. ...contactSearchParams.value
  1389. });
  1390. contactList.value = res.rows || [];
  1391. } catch (e) {
  1392. console.error('获取联系人列表失败:', e);
  1393. } finally {
  1394. contactLoading.value = false;
  1395. }
  1396. };
  1397. /** 搜索联系人 */
  1398. const handleContactSearch = () => {
  1399. getContactList();
  1400. };
  1401. /** 重置联系人搜索 */
  1402. const handleContactReset = () => {
  1403. contactSearchParams.value = {
  1404. userNo: '',
  1405. userName: ''
  1406. };
  1407. getContactList();
  1408. };
  1409. /** 新增联系人 */
  1410. const handleAddContact = () => {
  1411. // 重置表单
  1412. contactForm.value = {
  1413. supplierNo: detailData.value.supplierNo,
  1414. supplierId: route.query.id as any,
  1415. userNo: '',
  1416. userName: '',
  1417. phone: '',
  1418. roleNo: '',
  1419. departmentNo: '',
  1420. position: '',
  1421. isPrimaryContact: '0',
  1422. isRegister: '0',
  1423. email: '',
  1424. fax: '',
  1425. remark: ''
  1426. };
  1427. contactDialogTitle.value = '新增联系人';
  1428. contactDialogReadonly.value = false;
  1429. contactDialogVisible.value = true;
  1430. };
  1431. /** 查看联系人 */
  1432. const handleViewContact = async (row: any) => {
  1433. try {
  1434. const res = await getContact(row.id);
  1435. Object.assign(contactForm.value, res.data);
  1436. contactDialogTitle.value = '查看联系人';
  1437. contactDialogReadonly.value = true;
  1438. contactDialogVisible.value = true;
  1439. } catch (e) {
  1440. console.error('获取联系人详情失败:', e);
  1441. ElMessage.error('获取联系人详情失败');
  1442. }
  1443. };
  1444. /** 编辑联系人 */
  1445. const handleEditContact = async (row: any) => {
  1446. try {
  1447. const res = await getContact(row.id);
  1448. Object.assign(contactForm.value, res.data);
  1449. contactDialogTitle.value = '编辑联系人';
  1450. contactDialogReadonly.value = false;
  1451. contactDialogVisible.value = true;
  1452. } catch (e) {
  1453. console.error('获取联系人详情失败:', e);
  1454. ElMessage.error('获取联系人详情失败');
  1455. }
  1456. };
  1457. /** 提交联系人 */
  1458. const handleContactSubmit = async () => {
  1459. if (!contactFormRef.value) return;
  1460. contactFormRef.value.validate(async (valid: boolean) => {
  1461. if (!valid) return;
  1462. try {
  1463. contactSubmitLoading.value = true;
  1464. // 设置供应商编号和ID
  1465. if (!contactForm.value.supplierNo && detailData.value.supplierNo) {
  1466. contactForm.value.supplierNo = detailData.value.supplierNo;
  1467. }
  1468. if (!contactForm.value.supplierId && route.query.id) {
  1469. contactForm.value.supplierId = route.query.id as any;
  1470. }
  1471. if ((contactForm.value as any).id) {
  1472. // 更新
  1473. await updateContact(contactForm.value);
  1474. ElMessage.success('更新成功');
  1475. } else {
  1476. // 新增
  1477. await addContact(contactForm.value);
  1478. ElMessage.success('新增成功');
  1479. }
  1480. contactDialogVisible.value = false;
  1481. // 刷新联系人列表
  1482. await getContactList();
  1483. } catch (e) {
  1484. console.error('保存联系人失败:', e);
  1485. ElMessage.error('保存联系人失败');
  1486. } finally {
  1487. contactSubmitLoading.value = false;
  1488. }
  1489. });
  1490. };
  1491. /** 获取产品分类列表 */
  1492. const getProductCategories = async () => {
  1493. try {
  1494. const res = await getProductCategoryList();
  1495. productCategoryList.value = res.data || [];
  1496. console.log('产品分类列表:', productCategoryList.value);
  1497. // 获取分类列表后,再获取已选择的品目
  1498. await getSupplierSelectedCategories();
  1499. } catch (e) {
  1500. console.error('获取产品分类失败:', e);
  1501. }
  1502. };
  1503. /** 获取供应商已选择的品目 */
  1504. const getSupplierSelectedCategories = async () => {
  1505. const id = route.query.id as string;
  1506. if (!id) return;
  1507. try {
  1508. // 在edit模式下传递supplyStatus参数
  1509. const supplyStatus = isEditMode.value ? "4" : undefined;
  1510. const res = await getSupplierCategories(id, supplyStatus);
  1511. // 确保数据类型一致,转换为字符串数组
  1512. selectedCategories.value = (res.data || []).map(String);
  1513. console.log('供应商已选择的品目ID:', selectedCategories.value);
  1514. console.log('产品分类列表:', productCategoryList.value);
  1515. } catch (e) {
  1516. console.error('获取供应商品目失败:', e);
  1517. }
  1518. };
  1519. /** 保存供货类目 */
  1520. const handleSaveCategories = async () => {
  1521. try {
  1522. const id = route.query.id as string;
  1523. if (!id) {
  1524. ElMessage.error('供应商ID不存在');
  1525. return;
  1526. }
  1527. // 方案B:只提交必要字段,避免EditGroup校验因缺字段失败
  1528. await scmEditInfo({
  1529. id,
  1530. operatingCategory: selectedCategories.value.join(',')
  1531. } as any);
  1532. ElMessage.success('保存成功');
  1533. } catch (e) {
  1534. console.error('保存供货类目失败:', e);
  1535. ElMessage.error('保存失败');
  1536. }
  1537. };
  1538. /** 保存采购信息 */
  1539. const handleSavePurchaseInfo = async () => {
  1540. try {
  1541. const id = route.query.id as string;
  1542. if (!id) {
  1543. ElMessage.error('供应商ID不存在');
  1544. return;
  1545. }
  1546. // 调用保存采购信息接口
  1547. await savePurchaseInfo({
  1548. supplierId: id,
  1549. productManager: selectedProductManager.value,
  1550. purchaser: selectedBuyer.value
  1551. });
  1552. ElMessage.success('保存成功');
  1553. } catch (e) {
  1554. console.error('保存采购信息失败:', e);
  1555. ElMessage.error('保存失败');
  1556. }
  1557. };
  1558. /** 获取所有品牌列表 */
  1559. const getAllBrandList = async () => {
  1560. try {
  1561. const res = await listBrand({
  1562. pageNum: 1,
  1563. pageSize: 1000 // 获取所有品牌
  1564. });
  1565. allBrandList.value = res.rows || [];
  1566. console.log('品牌列表:', allBrandList.value);
  1567. } catch (e) {
  1568. console.error('获取品牌列表失败:', e);
  1569. }
  1570. };
  1571. /** 新增品牌 - 打开弹框 */
  1572. const handleAddBrand = () => {
  1573. // 复制当前已选择的品牌到临时列表
  1574. tempSelectedBrands.value = [...selectedBrands.value];
  1575. brandSearchKeyword.value = '';
  1576. // 显示所有品牌作为初始搜索结果
  1577. brandSearchResults.value = [...allBrandList.value];
  1578. brandDialogVisible.value = true;
  1579. };
  1580. /** 搜索品牌 */
  1581. const handleSearchBrand = async () => {
  1582. // 如果搜索关键词为空,显示所有品牌
  1583. if (!brandSearchKeyword.value.trim()) {
  1584. brandSearchResults.value = [...allBrandList.value];
  1585. return;
  1586. }
  1587. try {
  1588. brandSearchLoading.value = true;
  1589. const res = await listBrand({
  1590. brandName: brandSearchKeyword.value.trim(),
  1591. pageNum: 1,
  1592. pageSize: 50
  1593. });
  1594. brandSearchResults.value = res.rows || [];
  1595. if (brandSearchResults.value.length === 0) {
  1596. ElMessage.info('未找到匹配的品牌');
  1597. }
  1598. } catch (e) {
  1599. console.error('搜索品牌失败:', e);
  1600. ElMessage.error('搜索品牌失败');
  1601. } finally {
  1602. brandSearchLoading.value = false;
  1603. }
  1604. };
  1605. /** 添加品牌到列表 */
  1606. const handleAddBrandToList = (brand?: BrandVO) => {
  1607. // 如果没有传入品牌,则使用搜索关键词手动创建
  1608. if (!brand) {
  1609. if (!brandSearchKeyword.value.trim()) {
  1610. ElMessage.warning('请输入品牌名称或先搜索品牌');
  1611. return;
  1612. }
  1613. // 手动创建品牌对象(临时方案)
  1614. brand = {
  1615. id: brandSearchKeyword.value.trim(),
  1616. brandName: brandSearchKeyword.value.trim()
  1617. } as BrandVO;
  1618. }
  1619. // 检查品牌是否已存在
  1620. const exists = tempSelectedBrands.value.some(b => b.id === brand!.id);
  1621. if (exists) {
  1622. ElMessage.warning('该品牌已添加');
  1623. return;
  1624. }
  1625. // 添加品牌到临时列表
  1626. tempSelectedBrands.value.push(brand);
  1627. // 不清空搜索框和搜索结果,允许用户继续选择多个品牌
  1628. // brandSearchKeyword.value = '';
  1629. // brandSearchResults.value = [];
  1630. ElMessage.success(`已添加品牌:${brand.brandName}`);
  1631. };
  1632. /** 删除临时品牌 */
  1633. const handleRemoveTempBrand = (brand: BrandVO) => {
  1634. const index = tempSelectedBrands.value.findIndex(b => b.id === brand.id);
  1635. if (index > -1) {
  1636. tempSelectedBrands.value.splice(index, 1);
  1637. }
  1638. };
  1639. /** 删除已选择的品牌 */
  1640. const handleRemoveBrand = async (brand: BrandVO) => {
  1641. const index = selectedBrands.value.findIndex(b => b.id === brand.id);
  1642. if (index > -1) {
  1643. selectedBrands.value.splice(index, 1);
  1644. // 立即保存到后端
  1645. await saveBrandsToServer();
  1646. }
  1647. };
  1648. /** 品牌对话框关闭 */
  1649. const handleBrandDialogClose = () => {
  1650. brandDialogVisible.value = false;
  1651. brandSearchKeyword.value = '';
  1652. };
  1653. /** 提交品牌 */
  1654. const handleBrandSubmit = async () => {
  1655. try {
  1656. brandSubmitLoading.value = true;
  1657. // 更新主品牌列表
  1658. selectedBrands.value = [...tempSelectedBrands.value];
  1659. // 保存到服务器
  1660. await saveBrandsToServer();
  1661. ElMessage.success('保存成功');
  1662. brandDialogVisible.value = false;
  1663. } catch (e) {
  1664. console.error('保存品牌失败:', e);
  1665. ElMessage.error('保存失败');
  1666. } finally {
  1667. brandSubmitLoading.value = false;
  1668. }
  1669. };
  1670. /** 保存品牌到服务器 */
  1671. const saveBrandsToServer = async () => {
  1672. const id = route.query.id as string;
  1673. if (!id) {
  1674. ElMessage.error('供应商ID不存在');
  1675. return;
  1676. }
  1677. // 将品牌ID用逗号分隔保存
  1678. const brandIds = selectedBrands.value.map(brand => brand.id).join(',');
  1679. // 方案B:只提交必要字段,避免EditGroup校验因缺字段失败
  1680. await scmEditInfo({
  1681. id,
  1682. operatingBrand: brandIds
  1683. } as any);
  1684. };
  1685. /** 初始化品牌列表 - 从后端数据解析 */
  1686. const initBrandList = async () => {
  1687. if (detailData.value.operatingBrand) {
  1688. // 后端返回的是用逗号分隔的品牌ID
  1689. const brandIds = detailData.value.operatingBrand.split(',').filter(id => id.trim());
  1690. if (brandIds.length > 0) {
  1691. try {
  1692. // 根据品牌ID获取品牌详细信息
  1693. const brandPromises = brandIds.map(id => getBrand(id.trim()));
  1694. const brandResults = await Promise.allSettled(brandPromises);
  1695. selectedBrands.value = brandResults
  1696. .filter(result => result.status === 'fulfilled')
  1697. .map(result => (result as PromiseFulfilledResult<any>).value.data)
  1698. .filter(brand => brand); // 过滤掉空值
  1699. } catch (e) {
  1700. console.error('获取品牌详情失败:', e);
  1701. // 降级处理:如果获取品牌详情失败,使用ID作为显示名称
  1702. selectedBrands.value = brandIds.map(id => ({
  1703. id: id.trim(),
  1704. brandName: id.trim()
  1705. } as BrandVO));
  1706. }
  1707. }
  1708. }
  1709. };
  1710. /** 获取供货区域列表 */
  1711. const getSupplyAreaList = async () => {
  1712. const id = route.query.id as string;
  1713. if (!id) return;
  1714. try {
  1715. // 构建查询参数
  1716. const queryParams: any = {
  1717. supplierId: id,
  1718. pageNum: 1,
  1719. pageSize: 1000
  1720. };
  1721. // 只在编辑模式下添加 supplyStatus
  1722. if (isEditMode.value) {
  1723. queryParams.supplyStatus = "4";
  1724. }
  1725. const res = await listArea(queryParams);
  1726. // 保存原始数据,用于编辑时回显
  1727. savedAreaData.value = res.data || res.rows || [];
  1728. // 处理返回的数据,按层级组织
  1729. const areaData = savedAreaData.value;
  1730. // 第一步:先收集所有省份(用 areaId 做父子关联 key)
  1731. const provinceMap: any = {};
  1732. areaData.forEach((area: any) => {
  1733. if (area.level === '1' || area.level === 1) {
  1734. const pId = String(area.areaId ?? area.areaCode);
  1735. provinceMap[pId] = {
  1736. province: area.areaName,
  1737. city: ''
  1738. };
  1739. }
  1740. });
  1741. // 第二步:将城市添加到对应的省份
  1742. areaData.forEach((area: any) => {
  1743. if (area.level === '2' || area.level === 2) {
  1744. const pId = String(area.parentId ?? '0');
  1745. if (provinceMap[pId]) {
  1746. if (provinceMap[pId].city) {
  1747. provinceMap[pId].city += ',' + area.areaName;
  1748. } else {
  1749. provinceMap[pId].city = area.areaName;
  1750. }
  1751. }
  1752. }
  1753. });
  1754. // 转换为数组
  1755. supplyAreaList.value = Object.values(provinceMap).filter((item: any) =>
  1756. item.province || item.city
  1757. );
  1758. console.log('供货区域列表:', supplyAreaList.value);
  1759. console.log('保存的原始数据:', savedAreaData.value);
  1760. } catch (e) {
  1761. console.error('获取供货区域列表失败:', e);
  1762. }
  1763. };
  1764. /** 编辑供货区域 */
  1765. const handleEditSupplyArea = async () => {
  1766. supplyAreaDialogVisible.value = true;
  1767. // 等待对话框打开后再设置回显数据
  1768. await nextTick();
  1769. selectedSupplyAreaCodes.value = buildSupplyAreaCodesFromSavedData(savedAreaData.value);
  1770. };
  1771. /** 根据已保存的省/市数据生成 RegionCascader 需要的省/市ID列表 */
  1772. const buildSupplyAreaCodesFromSavedData = (areaData: any[]) => {
  1773. const codes = new Set<string>();
  1774. if (!areaData || areaData.length === 0) return [];
  1775. areaData.forEach((area: any) => {
  1776. const level = String(area.level);
  1777. const areaId = String(area.areaId ?? area.id ?? '');
  1778. if (!areaId) return;
  1779. if (level === '1' || level === '2') {
  1780. codes.add(areaId);
  1781. }
  1782. });
  1783. return Array.from(codes);
  1784. };
  1785. /** 根据省/市ID提取供货区域(省/市)展示字符串 */
  1786. const extractRegionDataFromSelectedProvinceCityIds = (selectedIds: string[]) => {
  1787. const provinces: any[] = supplyAreaOptions.value || [];
  1788. const provinceNames: string[] = [];
  1789. const cityNames: string[] = [];
  1790. const addedProvinces = new Set<string>();
  1791. const addedCities = new Set<string>();
  1792. selectedIds.forEach((id) => {
  1793. for (const province of provinces) {
  1794. if (String(province.value) === String(id)) {
  1795. if (!addedProvinces.has(String(province.value))) {
  1796. provinceNames.push(province.label);
  1797. addedProvinces.add(String(province.value));
  1798. }
  1799. return;
  1800. }
  1801. const cities = province?.children || [];
  1802. const hitCity = cities.find((c: any) => String(c.value) === String(id));
  1803. if (hitCity) {
  1804. if (!addedProvinces.has(String(province.value))) {
  1805. provinceNames.push(province.label);
  1806. addedProvinces.add(String(province.value));
  1807. }
  1808. if (!addedCities.has(String(hitCity.value))) {
  1809. cityNames.push(hitCity.label);
  1810. addedCities.add(String(hitCity.value));
  1811. }
  1812. return;
  1813. }
  1814. }
  1815. });
  1816. return {
  1817. provinces: provinceNames.join(','),
  1818. cities: cityNames.join(',')
  1819. };
  1820. };
  1821. /** 构建 areaList 数组(提交给后端:省(1)/市(2)) */
  1822. const buildAreaListFromSelectedProvinceCityIds = (selectedIds: string[]) => {
  1823. const provinces: any[] = supplyAreaOptions.value || [];
  1824. const areaList: any[] = [];
  1825. const addedProvinces = new Set<string>();
  1826. const addedCities = new Set<string>();
  1827. selectedIds.forEach((id) => {
  1828. for (const province of provinces) {
  1829. // 省
  1830. if (String(province.value) === String(id)) {
  1831. if (!addedProvinces.has(String(province.value))) {
  1832. areaList.push({
  1833. areaId: Number(province.value),
  1834. areaCode: province.areaCode || province.value,
  1835. areaName: province.label,
  1836. level: 1,
  1837. parentId: 0
  1838. });
  1839. addedProvinces.add(String(province.value));
  1840. }
  1841. return;
  1842. }
  1843. // 市
  1844. const cities = province?.children || [];
  1845. const hitCity = cities.find((c: any) => String(c.value) === String(id));
  1846. if (hitCity) {
  1847. // city 已选时确保 parent province 也存在(即使组件没回传,也兜底)
  1848. if (!addedProvinces.has(String(province.value))) {
  1849. areaList.push({
  1850. areaId: Number(province.value),
  1851. areaCode: province.areaCode || province.value,
  1852. areaName: province.label,
  1853. level: 1,
  1854. parentId: 0
  1855. });
  1856. addedProvinces.add(String(province.value));
  1857. }
  1858. if (!addedCities.has(String(hitCity.value))) {
  1859. areaList.push({
  1860. areaId: Number(hitCity.value),
  1861. areaCode: hitCity.areaCode || hitCity.value,
  1862. areaName: hitCity.label,
  1863. level: 2,
  1864. parentId: Number(province.value)
  1865. });
  1866. addedCities.add(String(hitCity.value));
  1867. }
  1868. return;
  1869. }
  1870. }
  1871. });
  1872. return areaList;
  1873. };
  1874. /** 提交供货区域 */
  1875. const handleSupplyAreaSubmit = async () => {
  1876. try {
  1877. supplyAreaSubmitLoading.value = true;
  1878. const id = route.query.id as string;
  1879. if (!id) {
  1880. ElMessage.error('供应商ID不存在');
  1881. return;
  1882. }
  1883. if (!detailData.value.supplierNo) {
  1884. ElMessage.error('供应商编号不存在,请先保存基础信息');
  1885. return;
  1886. }
  1887. console.log('提交前的选中省/市ID:', selectedSupplyAreaCodes.value);
  1888. // 构建 areaList 数组
  1889. const areaList = buildAreaListFromSelectedProvinceCityIds(selectedSupplyAreaCodes.value);
  1890. console.log('构建的 areaList:', areaList);
  1891. // 前端兜底去重:避免后端按“追加插入”导致重复省/市
  1892. // 只提交“新增的”省/市(已存在的省/市不重复提交)
  1893. const existedKeys = new Set<string>();
  1894. (savedAreaData.value || []).forEach((a: any) => {
  1895. const level = String(a.level);
  1896. const areaId = String(a.areaId ?? a.id ?? '');
  1897. if (!areaId) return;
  1898. if (level === '1' || level === '2') {
  1899. existedKeys.add(`${level}-${areaId}`);
  1900. }
  1901. });
  1902. const areaListToSubmit = (areaList || []).filter((a: any) => {
  1903. const level = String(a.level);
  1904. const areaId = String(a.areaId ?? '');
  1905. if (!areaId) return false;
  1906. return !existedKeys.has(`${level}-${areaId}`);
  1907. });
  1908. // 从选中的编码中提取区域信息(用于显示)
  1909. const regionData = extractRegionDataFromSelectedProvinceCityIds(selectedSupplyAreaCodes.value);
  1910. console.log('提取的区域数据:', regionData);
  1911. // 构建提交数据
  1912. const submitData: any = {
  1913. supplierId: id,
  1914. supplyNo: detailData.value.supplierNo,
  1915. areaList: areaListToSubmit
  1916. };
  1917. console.log('提交的数据:', submitData);
  1918. if (!submitData.areaList || submitData.areaList.length === 0) {
  1919. ElMessage.success('保存成功');
  1920. supplyAreaDialogVisible.value = false;
  1921. await getSupplyAreaList();
  1922. return;
  1923. }
  1924. await addArea(submitData);
  1925. // 更新本地数据
  1926. (detailData.value as any).province = regionData.provinces;
  1927. (detailData.value as any).city = regionData.cities;
  1928. // edit模式下,保存供货区域后需要同步更新主表信息(保留其他字段的修改)
  1929. if (isEditMode.value) {
  1930. try {
  1931. const tempRes = await getInfoTemporary(id);
  1932. let mainSubmitData: any;
  1933. if (tempRes.data) {
  1934. // 临时表已有记录,使用临时表数据作为基础
  1935. const tempData = tempRes.data;
  1936. mainSubmitData = {
  1937. ...tempData, // 临时表中的数据(保留之前的所有修改)
  1938. ...detailData.value, // 当前页面的数据
  1939. supplierType: String(detailData.value.supplierType),
  1940. cooperateLevel: String(detailData.value.cooperateLevel),
  1941. };
  1942. } else {
  1943. // 临时表没有记录,使用当前数据
  1944. mainSubmitData = {
  1945. ...detailData.value,
  1946. supplierType: String(detailData.value.supplierType),
  1947. cooperateLevel: String(detailData.value.cooperateLevel),
  1948. };
  1949. }
  1950. // 删除后端返回的展示字段
  1951. delete mainSubmitData.supplierTypeName;
  1952. delete mainSubmitData.cooperateLevelName;
  1953. delete mainSubmitData.membershipSizeName;
  1954. delete mainSubmitData.industrCategoryName;
  1955. delete mainSubmitData.productManager;
  1956. delete mainSubmitData.buyer;
  1957. delete mainSubmitData.brandName;
  1958. delete mainSubmitData.province;
  1959. delete mainSubmitData.city;
  1960. // 更新主表信息
  1961. await updateInfo(mainSubmitData);
  1962. } catch (e) {
  1963. console.error('更新主表信息失败:', e);
  1964. }
  1965. }
  1966. ElMessage.success('保存成功');
  1967. supplyAreaDialogVisible.value = false;
  1968. await getSupplyAreaList();
  1969. } catch (e) {
  1970. console.error('保存供货区域失败:', e);
  1971. ElMessage.error('保存失败');
  1972. } finally {
  1973. supplyAreaSubmitLoading.value = false;
  1974. }
  1975. };
  1976. /** 供货区域选择变化 */
  1977. const handleSupplyAreaChange = (value: any) => {
  1978. console.log('供货区域选择变化:', value);
  1979. };
  1980. /** 格式化日期 */
  1981. const formatDate = (date: string | Date) => {
  1982. if (!date) return '-';
  1983. const d = new Date(date);
  1984. return d.toISOString().split('T')[0];
  1985. };
  1986. /** 获取合同状态类型 */
  1987. const getContractStatusType = (status: number) => {
  1988. const statusMap = {
  1989. 0: 'info', // 草稿
  1990. 1: 'success', // 已生效
  1991. 2: 'warning', // 已到期
  1992. 3: 'danger', // 已终止
  1993. 4: 'danger' // 已作废
  1994. };
  1995. return statusMap[status] || 'info';
  1996. };
  1997. /** 获取合同状态文本 */
  1998. const getContractStatusText = (status: number) => {
  1999. const statusMap = {
  2000. 0: '待审核',
  2001. 1: '生效',
  2002. 2: '失效'
  2003. };
  2004. return statusMap[status] || '未知';
  2005. };
  2006. /** 获取合同类型文本 */
  2007. const getContractTypeText = (type: string | number) => {
  2008. const typeMap = {
  2009. '0': '年度合作',
  2010. '1': '项目采购',
  2011. '2': '其他合作',
  2012. };
  2013. return typeMap[type] || '未知';
  2014. };
  2015. /** 获取授权状态文本 */
  2016. const getAuthorizedStatusText = (status: string) => {
  2017. const statusMap = {
  2018. '0': '待审核',
  2019. '1': '已生效',
  2020. '2': '已过期'
  2021. };
  2022. return statusMap[status] || '未知';
  2023. };
  2024. /** 搜索合同 */
  2025. const handleContractSearch = () => {
  2026. console.log('搜索合同:', contractSearchParams.value);
  2027. getContractList();
  2028. };
  2029. /** 重置合同搜索 */
  2030. const handleContractReset = () => {
  2031. contractSearchParams.value = {
  2032. contractNo: '',
  2033. contractName: '',
  2034. contractType: '',
  2035. contractStartTime: '',
  2036. contractEndTime: '',
  2037. contractStatus: ''
  2038. };
  2039. getContractList();
  2040. };
  2041. const wanToYuanString = (value: unknown): string => {
  2042. const raw = String(value ?? '').trim();
  2043. if (!raw) return '';
  2044. const normalized = raw.replace(/,/g, '');
  2045. const match = normalized.match(/^(-?)(\d+)(?:\.(\d+))?$/);
  2046. if (!match) return '';
  2047. const sign = match[1] ? '-' : '';
  2048. const intPart = match[2] || '0';
  2049. const fracRaw = match[3] || '';
  2050. const fracPart = (fracRaw + '0000').slice(0, 4);
  2051. const base = BigInt(intPart) * 10000n + BigInt(fracPart);
  2052. return sign ? '-' + base.toString() : base.toString();
  2053. };
  2054. const yuanToWanString = (value: unknown): string => {
  2055. const raw = String(value ?? '').trim();
  2056. if (!raw) return '';
  2057. const normalized = raw.replace(/,/g, '');
  2058. const match = normalized.match(/^(-?)(\d+)$/);
  2059. if (!match) return '';
  2060. const sign = match[1] ? '-' : '';
  2061. const amount = BigInt(match[2] || '0');
  2062. const intPart = amount / 10000n;
  2063. const fracPart = amount % 10000n;
  2064. const fracPadded = fracPart.toString().padStart(4, '0');
  2065. const fracTrimmed = fracPadded.replace(/0+$/, '');
  2066. const result = fracTrimmed ? `${intPart.toString()}.${fracTrimmed}` : intPart.toString();
  2067. return sign ? '-' + result : result;
  2068. };
  2069. /** 获取合同列表 */
  2070. const getContractList = async () => {
  2071. const id = route.query.id as string;
  2072. if (!id) return;
  2073. try {
  2074. // 构建查询参数,排除日期字段
  2075. const { contractStartTime, contractEndTime, ...otherParams } = contractSearchParams.value;
  2076. const params: any = {
  2077. pageNum: contractPagination.value.pageNum,
  2078. pageSize: contractPagination.value.pageSize,
  2079. ...otherParams
  2080. };
  2081. // 如果有开始时间,格式化为 YYYY-MM-DD
  2082. if (contractStartTime) {
  2083. const date = new Date(contractStartTime);
  2084. params.contractStartTime = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
  2085. }
  2086. // 如果有结束时间,格式化为 YYYY-MM-DD
  2087. if (contractEndTime) {
  2088. const date = new Date(contractEndTime);
  2089. params.contractEndTime = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
  2090. }
  2091. console.log('合同查询参数:', params); // 添加日志方便调试
  2092. const res = await getSupplierContractsById(id, params);
  2093. contractList.value = (res.rows || []).map((row: any) => ({
  2094. ...row,
  2095. contractAmount: yuanToWanString(row?.contractAmount)
  2096. }));
  2097. contractPagination.value.total = res.total || 0;
  2098. } catch (e) {
  2099. console.error('获取合同列表失败:', e);
  2100. }
  2101. };
  2102. /** 查看附件 */
  2103. const handleViewAttachment = (row: any) => {
  2104. console.log('查看附件:', row);
  2105. if (!row.contractAttachment) {
  2106. ElMessage.warning('该合同没有附件');
  2107. return;
  2108. }
  2109. try {
  2110. // 如果 contractAttachment 是文件ID(数字),使用 oss 方法下载
  2111. if (/^\d+$/.test(row.contractAttachment)) {
  2112. download.oss(row.contractAttachment);
  2113. } else {
  2114. // 如果是文件路径或URL,直接下载
  2115. const link = document.createElement('a');
  2116. link.href = row.contractAttachment;
  2117. link.download = `合同附件_${row.contractName || row.contractNo || 'attachment'}`;
  2118. document.body.appendChild(link);
  2119. link.click();
  2120. document.body.removeChild(link);
  2121. }
  2122. } catch (e) {
  2123. console.error('下载附件失败:', e);
  2124. ElMessage.error('下载附件失败');
  2125. }
  2126. };
  2127. /** 查看合同 */
  2128. const handleViewContract = async (row: any) => {
  2129. try {
  2130. const res = await getContract(row.id);
  2131. Object.assign(contractForm.value, res.data);
  2132. contractForm.value.contractAmount = yuanToWanString((res.data as any)?.contractAmount);
  2133. contractDialogTitle.value = '查看合同';
  2134. contractDialogReadonly.value = true;
  2135. contractDialogVisible.value = true;
  2136. } catch (e) {
  2137. console.error('获取合同详情失败:', e);
  2138. ElMessage.error('获取合同详情失败');
  2139. }
  2140. };
  2141. /** 编辑合同 */
  2142. const handleEditContract = async (row: any) => {
  2143. try {
  2144. const res = await getContract(row.id);
  2145. Object.assign(contractForm.value, res.data);
  2146. contractForm.value.contractAmount = yuanToWanString((res.data as any)?.contractAmount);
  2147. contractDialogTitle.value = '编辑合同';
  2148. contractDialogReadonly.value = false;
  2149. contractDialogVisible.value = true;
  2150. } catch (e) {
  2151. console.error('获取合同详情失败:', e);
  2152. ElMessage.error('获取合同详情失败');
  2153. }
  2154. };
  2155. /** 获取合同类型字典 */
  2156. const getContractTypeDict = async () => {
  2157. try {
  2158. const res = await getDictData('contract_type');
  2159. contractTypeDict.value = res.data || [];
  2160. } catch (e) {
  2161. console.error('获取合同类型字典失败:', e);
  2162. }
  2163. };
  2164. /** 获取税率列表 */
  2165. const getTaxRateData = async () => {
  2166. try {
  2167. const res = await getTaxRateList();
  2168. taxRateList.value = res.rows || res.data || [];
  2169. } catch (e) {
  2170. console.error('获取税率列表失败:', e);
  2171. }
  2172. };
  2173. /** 获取结算方式列表 */
  2174. const getSettlementMethodData = async () => {
  2175. try {
  2176. const res = await getSettlementMethodList();
  2177. settlementMethodList.value = res.rows || res.data || [];
  2178. } catch (e) {
  2179. console.error('获取结算方式列表失败:', e);
  2180. }
  2181. };
  2182. /** 新增合同 */
  2183. const handleAddContract = () => {
  2184. // 重置表单
  2185. contractForm.value = {
  2186. supplierNo: detailData.value.supplierNo,
  2187. supplierId: route.query.id as any,
  2188. contractNo: '',
  2189. contractName: '',
  2190. contractType: '',
  2191. contractStartTime: '',
  2192. contractEndTime: '',
  2193. contractStatus: 0,
  2194. demandReminderTime: 4,
  2195. taxRate: undefined,
  2196. contractAmount: undefined,
  2197. contractDescription: '',
  2198. contractAttachment: '',
  2199. settlementMethod: '',
  2200. invoiceType: ''
  2201. };
  2202. contractDialogTitle.value = '添加合同';
  2203. contractDialogReadonly.value = false;
  2204. contractDialogVisible.value = true;
  2205. };
  2206. /** 提交合同 */
  2207. const handleContractSubmit = async () => {
  2208. if (!contractFormRef.value) return;
  2209. contractFormRef.value.validate(async (valid: boolean) => {
  2210. if (!valid) return;
  2211. try {
  2212. contractSubmitLoading.value = true;
  2213. // 设置供应商编号和ID
  2214. if (!contractForm.value.supplierNo && detailData.value.supplierNo) {
  2215. contractForm.value.supplierNo = detailData.value.supplierNo;
  2216. }
  2217. if (!contractForm.value.supplierId && route.query.id) {
  2218. contractForm.value.supplierId = route.query.id as any;
  2219. }
  2220. const payload = {
  2221. ...contractForm.value,
  2222. contractAmount: wanToYuanString(contractForm.value.contractAmount)
  2223. };
  2224. if ((contractForm.value as any).id) {
  2225. // 更新
  2226. await updateContract(payload);
  2227. ElMessage.success('更新成功');
  2228. } else {
  2229. // 新增
  2230. await addContract(payload);
  2231. ElMessage.success('新增成功');
  2232. }
  2233. contractDialogVisible.value = false;
  2234. // 刷新合同列表
  2235. await getContractList();
  2236. } catch (e) {
  2237. console.error('保存合同失败:', e);
  2238. ElMessage.error('保存合同失败');
  2239. } finally {
  2240. contractSubmitLoading.value = false;
  2241. }
  2242. });
  2243. };
  2244. /** 分页大小改变 */
  2245. const handleContractSizeChange = (size: number) => {
  2246. contractPagination.value.pageSize = size;
  2247. getContractList();
  2248. };
  2249. /** 当前页改变 */
  2250. const handleContractCurrentChange = (page: number) => {
  2251. contractPagination.value.pageNum = page;
  2252. getContractList();
  2253. };
  2254. /** 查看付款信息 */
  2255. const handleViewPayment = async (row: any) => {
  2256. try {
  2257. const res = await getBank(row.id);
  2258. Object.assign(paymentForm.value, res.data);
  2259. paymentDialogTitle.value = '查看付款信息';
  2260. paymentDialogReadonly.value = true;
  2261. paymentDialogVisible.value = true;
  2262. } catch (e) {
  2263. console.error('获取付款信息详情失败:', e);
  2264. ElMessage.error('获取付款信息详情失败');
  2265. }
  2266. };
  2267. /** 编辑付款信息 */
  2268. const handleEditPayment = async (row: any) => {
  2269. try {
  2270. const res = await getBank(row.id);
  2271. Object.assign(paymentForm.value, res.data);
  2272. // 从供应商详情中获取企业工商名称和工商地址
  2273. paymentForm.value.businessName = detailData.value.businessName || '';
  2274. paymentForm.value.businessAddress = detailData.value.businessAddress || '';
  2275. paymentDialogTitle.value = '编辑付款信息';
  2276. paymentDialogReadonly.value = false;
  2277. paymentDialogVisible.value = true;
  2278. } catch (e) {
  2279. console.error('获取付款信息详情失败:', e);
  2280. ElMessage.error('获取付款信息详情失败');
  2281. }
  2282. };
  2283. /** 新增付款信息 */
  2284. const handleAddPayment = () => {
  2285. // 检查是否已保存供应商基本信息
  2286. if (!detailData.value.id || !detailData.value.supplierNo) {
  2287. ElMessage.warning('请先保存供应商基本信息后再新增付款信息');
  2288. return;
  2289. }
  2290. // 重置表单
  2291. paymentForm.value = {
  2292. num: undefined,
  2293. supplierNo: detailData.value.supplierNo,
  2294. supplierId: detailData.value.id,
  2295. bankNum: '',
  2296. bankInfoNo: undefined,
  2297. bankName: '',
  2298. bankNo: '',
  2299. isture: '1',
  2300. circlesName: '',
  2301. phone: '',
  2302. invoiceTypeNo: undefined,
  2303. invoiceTypeName: '',
  2304. businessName: detailData.value.businessName || '',
  2305. businessAddress: detailData.value.businessAddress || ''
  2306. };
  2307. paymentDialogTitle.value = '新增付款信息';
  2308. paymentDialogReadonly.value = false;
  2309. paymentDialogVisible.value = true;
  2310. };
  2311. /** 发票类型选择改变 */
  2312. const handleInvoiceTypeChange = (value: string | number) => {
  2313. const selectedInvoice = invoiceTypeList.value.find(item => item.id === value);
  2314. if (selectedInvoice) {
  2315. paymentForm.value.invoiceTypeNo = selectedInvoice.id;
  2316. paymentForm.value.invoiceTypeName = selectedInvoice.invoiceTypeName;
  2317. }
  2318. };
  2319. /** 银行选择改变 */
  2320. const handleBankChange = (value: string | number) => {
  2321. const selectedBank = systemBankList.value.find(item => item.id === value);
  2322. if (selectedBank) {
  2323. paymentForm.value.bankInfoNo = selectedBank.id;
  2324. paymentForm.value.bankName = selectedBank.bnName;
  2325. }
  2326. };
  2327. /** 提交付款信息 */
  2328. const handlePaymentSubmit = async () => {
  2329. if (!paymentFormRef.value) return;
  2330. paymentFormRef.value.validate(async (valid: boolean) => {
  2331. if (!valid) return;
  2332. try {
  2333. paymentSubmitLoading.value = true;
  2334. // 设置供应商编号和ID
  2335. if (!paymentForm.value.supplierNo && detailData.value.supplierNo) {
  2336. paymentForm.value.supplierNo = detailData.value.supplierNo;
  2337. }
  2338. if (!paymentForm.value.supplierId && detailData.value.id) {
  2339. paymentForm.value.supplierId = detailData.value.id;
  2340. }
  2341. if ((paymentForm.value as any).id) {
  2342. // 更新
  2343. await updateBank(paymentForm.value);
  2344. ElMessage.success('更新成功');
  2345. } else {
  2346. // 新增
  2347. await addBank(paymentForm.value);
  2348. ElMessage.success('新增成功');
  2349. }
  2350. paymentDialogVisible.value = false;
  2351. // 刷新付款信息列表
  2352. await getPaymentInfo();
  2353. } catch (e) {
  2354. console.error('保存付款信息失败:', e);
  2355. ElMessage.error('保存付款信息失败');
  2356. } finally {
  2357. paymentSubmitLoading.value = false;
  2358. }
  2359. });
  2360. };
  2361. /** 获取地址列表 */
  2362. const getAddressList = async () => {
  2363. const id = route.query.id as string;
  2364. // add状态不查询地址列表(新增时没有地址)
  2365. if (!id || isAddMode.value) {
  2366. addressList.value = [];
  2367. return;
  2368. }
  2369. try {
  2370. const res = await listAddress({
  2371. supplierId: id, // 使用supplierId作为查询条件
  2372. pageNum: 1,
  2373. pageSize: 1000
  2374. });
  2375. addressList.value = res.data || res.rows || [];
  2376. } catch (e) {
  2377. console.error('获取地址列表失败:', e);
  2378. }
  2379. };
  2380. /** 新增地址 */
  2381. const handleAddAddress = () => {
  2382. // 重置表单
  2383. addressForm.value = {
  2384. supplierNo: detailData.value.supplierNo,
  2385. supplierId: detailData.value.id,
  2386. shipperName: '',
  2387. shipperPhone: '',
  2388. shippingPostCode: '',
  2389. shippingProvincial: '',
  2390. shippingCity: '',
  2391. shippingCounty: '',
  2392. shippingAddress: '',
  2393. isSelf: 0
  2394. };
  2395. selectedAddressRegion.value = [];
  2396. addressDialogTitle.value = '添加地址';
  2397. addressDialogReadonly.value = false;
  2398. addressDialogVisible.value = true;
  2399. };
  2400. /** 查看地址 */
  2401. const handleViewAddress = async (row: any) => {
  2402. try {
  2403. const res = await getAddress(row.id);
  2404. Object.assign(addressForm.value, res.data);
  2405. // 回显省市区选择器
  2406. if (res.data.shippingProvincial && res.data.shippingCity && res.data.shippingCounty) {
  2407. const province = res.data.shippingProvincial;
  2408. const city = res.data.shippingCity;
  2409. const county = res.data.shippingCounty;
  2410. // 根据省市区名称查找对应的代码
  2411. const provinceItem = regionOptions.value.find((p: any) => p.label === province);
  2412. if (provinceItem) {
  2413. const cityItem = provinceItem.children?.find((c: any) => c.label === city);
  2414. if (cityItem) {
  2415. const districtItem = cityItem.children?.find((d: any) => d.label === county);
  2416. if (districtItem) {
  2417. selectedAddressRegion.value = [provinceItem.value, cityItem.value, districtItem.value];
  2418. }
  2419. }
  2420. }
  2421. }
  2422. addressDialogTitle.value = '查看地址';
  2423. addressDialogReadonly.value = true;
  2424. addressDialogVisible.value = true;
  2425. } catch (e) {
  2426. console.error('获取地址详情失败:', e);
  2427. ElMessage.error('获取地址详情失败');
  2428. }
  2429. };
  2430. /** 编辑地址 */
  2431. const handleEditAddress = async (row: any) => {
  2432. try {
  2433. const res = await getAddress(row.id);
  2434. Object.assign(addressForm.value, res.data);
  2435. // 回显省市区选择器
  2436. if (res.data.shippingProvincial && res.data.shippingCity && res.data.shippingCounty) {
  2437. const province = res.data.shippingProvincial;
  2438. const city = res.data.shippingCity;
  2439. const county = res.data.shippingCounty;
  2440. // 根据省市区名称查找对应的代码
  2441. const provinceItem = regionOptions.value.find((p: any) => p.label === province);
  2442. if (provinceItem) {
  2443. const cityItem = provinceItem.children?.find((c: any) => c.label === city);
  2444. if (cityItem) {
  2445. const districtItem = cityItem.children?.find((d: any) => d.label === county);
  2446. if (districtItem) {
  2447. selectedAddressRegion.value = [provinceItem.value, cityItem.value, districtItem.value];
  2448. }
  2449. }
  2450. }
  2451. }
  2452. addressDialogTitle.value = '编辑地址';
  2453. addressDialogReadonly.value = false;
  2454. addressDialogVisible.value = true;
  2455. } catch (e) {
  2456. console.error('获取地址详情失败:', e);
  2457. ElMessage.error('获取地址详情失败');
  2458. }
  2459. };
  2460. /** 删除地址 */
  2461. const handleDeleteAddress = async (row: any) => {
  2462. try {
  2463. await ElMessageBox.confirm('确认删除该地址吗?', '提示', {
  2464. confirmButtonText: '确认',
  2465. cancelButtonText: '取消',
  2466. type: 'warning'
  2467. });
  2468. await delAddress(row.id);
  2469. ElMessage.success('删除成功');
  2470. // 刷新地址列表
  2471. getAddressList();
  2472. } catch (e: any) {
  2473. if (e !== 'cancel') {
  2474. console.error('删除地址失败:', e);
  2475. ElMessage.error('删除地址失败');
  2476. }
  2477. }
  2478. };
  2479. /** 地址省市区改变事件 */
  2480. const handleAddressRegionChange = (value: string[]) => {
  2481. if (value && value.length === 3) {
  2482. // value 是 [省代码, 市代码, 区代码]
  2483. const [provinceCode, cityCode, districtCode] = value;
  2484. // 从 regionOptions 中查找对应的名称
  2485. let provinceName = '';
  2486. let cityName = '';
  2487. let districtName = '';
  2488. const province = regionOptions.value.find((p: any) => p.value === provinceCode);
  2489. if (province) {
  2490. provinceName = province.label;
  2491. const city = province.children?.find((c: any) => c.value === cityCode);
  2492. if (city) {
  2493. cityName = city.label;
  2494. const district = city.children?.find((d: any) => d.value === districtCode);
  2495. if (district) {
  2496. districtName = district.label;
  2497. }
  2498. }
  2499. }
  2500. // 更新 addressForm
  2501. addressForm.value.shippingProvincial = provinceName;
  2502. addressForm.value.shippingCity = cityName;
  2503. addressForm.value.shippingCounty = districtName;
  2504. } else {
  2505. // 清空
  2506. addressForm.value.shippingProvincial = '';
  2507. addressForm.value.shippingCity = '';
  2508. addressForm.value.shippingCounty = '';
  2509. }
  2510. };
  2511. /** 提交地址 */
  2512. const handleAddressSubmit = async () => {
  2513. if (!addressFormRef.value) return;
  2514. addressFormRef.value.validate(async (valid: boolean) => {
  2515. if (!valid) return;
  2516. try {
  2517. addressSubmitLoading.value = true;
  2518. // 设置供应商编号和ID
  2519. if (!addressForm.value.supplierNo && detailData.value.supplierNo) {
  2520. addressForm.value.supplierNo = detailData.value.supplierNo;
  2521. }
  2522. if (!addressForm.value.supplierId && detailData.value.id) {
  2523. addressForm.value.supplierId = detailData.value.id;
  2524. }
  2525. if ((addressForm.value as any).id) {
  2526. // 更新
  2527. await updateAddress(addressForm.value);
  2528. ElMessage.success('更新成功');
  2529. } else {
  2530. // 新增
  2531. await addAddress(addressForm.value);
  2532. ElMessage.success('新增成功');
  2533. }
  2534. addressDialogVisible.value = false;
  2535. // 刷新地址列表
  2536. await getAddressList();
  2537. } catch (e) {
  2538. console.error('保存地址失败:', e);
  2539. ElMessage.error('保存地址失败');
  2540. } finally {
  2541. addressSubmitLoading.value = false;
  2542. }
  2543. });
  2544. };
  2545. onMounted(async () => {
  2546. // 先初始化省市区数据
  2547. initRegionOptions();
  2548. // 初始化供货区域选项数据
  2549. initSupplyAreaOptions();
  2550. // 并行加载所有下拉框选项数据
  2551. await Promise.all([
  2552. getStaffOptions(),
  2553. getCompanyOptions(), // 获取公司下拉选项
  2554. getEnterpriseScaleOptions(), // 获取企业规模下拉选项
  2555. getIndustryCategoryOptions(), // 获取行业类别下拉选项
  2556. getSupplierLevelOptions(), // 获取供应商等级下拉选项
  2557. getSupplierTypeOptions(), // 获取供应商类型下拉选项
  2558. getContractTypeDict(), // 获取合同类型字典
  2559. getTaxRateData(), // 获取税率列表
  2560. getSettlementMethodData(), // 获取结算方式列表
  2561. getInvoiceTypeData(), // 获取发票类型列表
  2562. getAllBrandList(), // 获取所有品牌列表
  2563. getInvoiceTypeData(), // 获取开票类型列表
  2564. getSystemBankData() // 获取银行列表
  2565. ]);
  2566. // 下拉框选项加载完成后,再获取详情数据,确保下拉框能正确显示文字标签
  2567. await getDetail();
  2568. // 只有在编辑模式或基础信息已保存时才加载其他数据
  2569. const id = route.query.id as string;
  2570. const mode = route.query.mode as string;
  2571. if (id && mode !== 'add') {
  2572. getContactList();
  2573. getProductCategories(); // 这个方法内部会调用 getSupplierSelectedCategories
  2574. getContractList(); // 获取合同列表
  2575. getPaymentInfo(); // 获取付款信息
  2576. getAuthorizationList(); // 获取授权详情列表
  2577. getAddressList(); // 获取地址列表
  2578. getSupplyAreaList(); // 获取供货区域列表
  2579. }
  2580. });
  2581. </script>
  2582. <style scoped>
  2583. .app-container {
  2584. background: #f0f2f5;
  2585. min-height: 100vh;
  2586. padding: 0;
  2587. }
  2588. .detail-header {
  2589. background: #fff;
  2590. padding: 16px 24px;
  2591. display: flex;
  2592. align-items: center;
  2593. border-bottom: 1px solid #e8e8e8;
  2594. margin-bottom: 0;
  2595. }
  2596. .back-icon {
  2597. font-size: 18px;
  2598. cursor: pointer;
  2599. margin-right: 12px;
  2600. color: #666;
  2601. }
  2602. .back-icon:hover {
  2603. color: #409eff;
  2604. }
  2605. .header-title {
  2606. font-size: 16px;
  2607. font-weight: 500;
  2608. color: #333;
  2609. }
  2610. .detail-tabs {
  2611. background: #fff;
  2612. margin: 0;
  2613. padding: 0 24px;
  2614. margin-top: 16px;
  2615. }
  2616. .detail-tabs :deep(.el-tabs__header) {
  2617. margin: 0;
  2618. border-bottom: 1px solid #e8e8e8;
  2619. }
  2620. .detail-tabs :deep(.el-tabs__nav-wrap) {
  2621. padding: 0;
  2622. }
  2623. .detail-tabs :deep(.el-tabs__item) {
  2624. height: 48px;
  2625. line-height: 48px;
  2626. font-size: 14px;
  2627. color: #666;
  2628. }
  2629. .detail-tabs :deep(.el-tabs__item.is-active) {
  2630. color: #409eff;
  2631. font-weight: 500;
  2632. }
  2633. .app-container :deep(.tab-content) {
  2634. padding: 24px;
  2635. background: #fff;
  2636. }
  2637. .app-container :deep(.info-section) {
  2638. margin-bottom: 40px;
  2639. }
  2640. .app-container :deep(.section-title) {
  2641. font-size: 15px;
  2642. font-weight: 500;
  2643. color: #333;
  2644. margin-bottom: 24px;
  2645. padding-bottom: 12px;
  2646. border-bottom: 1px solid #e8e8e8;
  2647. }
  2648. .app-container :deep(.section-title-row) {
  2649. display: flex;
  2650. justify-content: space-between;
  2651. align-items: center;
  2652. margin-bottom: 24px;
  2653. padding-bottom: 12px;
  2654. border-bottom: 1px solid #e8e8e8;
  2655. }
  2656. .app-container :deep(.section-title-left) {
  2657. display: flex;
  2658. align-items: center;
  2659. }
  2660. .app-container :deep(.section-title-text) {
  2661. font-size: 15px;
  2662. font-weight: 500;
  2663. color: #409eff;
  2664. }
  2665. .app-container :deep(.section-title-divider) {
  2666. margin: 0 8px;
  2667. color: #999;
  2668. }
  2669. .app-container :deep(.supplier-no) {
  2670. color: #409eff;
  2671. font-weight: normal;
  2672. font-size: 14px;
  2673. }
  2674. .detail-form :deep(.el-form-item) {
  2675. margin-bottom: 18px;
  2676. }
  2677. .detail-form :deep(.el-form-item__label) {
  2678. color: #666;
  2679. font-size: 14px;
  2680. }
  2681. .detail-form :deep(.el-form-item__label::before) {
  2682. color: #f56c6c;
  2683. }
  2684. .app-container :deep(.form-row) {
  2685. margin-bottom: 20px;
  2686. }
  2687. .app-container :deep(.form-item) {
  2688. display: flex;
  2689. align-items: flex-start;
  2690. line-height: 32px;
  2691. font-size: 14px;
  2692. }
  2693. .app-container :deep(.form-item .label) {
  2694. color: #666;
  2695. min-width: 100px;
  2696. flex-shrink: 0;
  2697. }
  2698. .app-container :deep(.form-item .value) {
  2699. color: #333;
  2700. flex: 1;
  2701. word-break: break-all;
  2702. }
  2703. .app-container :deep(.category-group) {
  2704. display: flex;
  2705. flex-wrap: wrap;
  2706. gap: 16px;
  2707. }
  2708. .app-container :deep(.category-group .el-checkbox) {
  2709. margin-right: 0;
  2710. margin-bottom: 12px;
  2711. }
  2712. .app-container :deep(.supply-brand) {
  2713. font-size: 14px;
  2714. color: #333;
  2715. padding: 8px 0;
  2716. }
  2717. .app-container :deep(.brand-input-wrapper) {
  2718. margin-top: 12px;
  2719. }
  2720. .app-container :deep(.brand-dialog .el-dialog__header) {
  2721. padding: 16px 20px 12px;
  2722. border-bottom: 1px solid #ebeef5;
  2723. }
  2724. .app-container :deep(.brand-dialog .el-dialog__title) {
  2725. font-size: 16px;
  2726. font-weight: 600;
  2727. color: #303133;
  2728. }
  2729. .app-container :deep(.brand-dialog .el-dialog__body) {
  2730. padding: 16px 20px 18px;
  2731. }
  2732. .app-container :deep(.brand-dialog__search) {
  2733. padding: 12px 12px 8px;
  2734. background: #f7f9fc;
  2735. border: 1px solid #ebeef5;
  2736. border-radius: 10px;
  2737. margin-bottom: 14px;
  2738. }
  2739. .app-container :deep(.brand-dialog__search .el-input__wrapper) {
  2740. border-radius: 8px;
  2741. }
  2742. .app-container :deep(.brand-search-results) {
  2743. border-radius: 10px;
  2744. border: 1px solid #ebeef5;
  2745. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
  2746. }
  2747. .app-container :deep(.brand-display-wrapper) {
  2748. margin-top: 12px;
  2749. min-height: 40px;
  2750. padding: 8px;
  2751. background: #f5f7fa;
  2752. border-radius: 4px;
  2753. }
  2754. .app-container :deep(.brand-tags-container) {
  2755. min-height: 80px;
  2756. padding: 16px;
  2757. background: #f5f7fa;
  2758. border-radius: 4px;
  2759. border: 1px solid #e4e7ed;
  2760. }
  2761. .app-container :deep(.brand-search-results) {
  2762. max-height: 300px;
  2763. overflow-y: auto;
  2764. margin-bottom: 20px;
  2765. border: 1px solid #e4e7ed;
  2766. border-radius: 4px;
  2767. }
  2768. .app-container :deep(.search-results-title) {
  2769. padding: 10px 16px;
  2770. background: #f7f9fc;
  2771. font-weight: 500;
  2772. font-size: 14px;
  2773. color: #606266;
  2774. border-bottom: 1px solid #ebeef5;
  2775. }
  2776. .app-container :deep(.brand-result-item) {
  2777. padding: 12px 16px;
  2778. cursor: pointer;
  2779. display: flex;
  2780. justify-content: space-between;
  2781. align-items: center;
  2782. border-bottom: 1px solid #f2f4f7;
  2783. transition: background-color 0.2s;
  2784. }
  2785. .app-container :deep(.brand-result-item:hover) {
  2786. background-color: #f7f9fc;
  2787. }
  2788. .app-container :deep(.brand-result-item:active) {
  2789. background-color: #eef5ff;
  2790. }
  2791. .app-container :deep(.brand-result-item:last-child) {
  2792. border-bottom: none;
  2793. }
  2794. .app-container :deep(.brand-result-name) {
  2795. font-size: 14px;
  2796. color: #303133;
  2797. font-weight: 500;
  2798. }
  2799. .app-container :deep(.brand-result-no) {
  2800. font-size: 12px;
  2801. color: #909399;
  2802. }
  2803. .app-container :deep(.selected-brands-section) {
  2804. margin-top: 20px;
  2805. }
  2806. .app-container :deep(.section-label) {
  2807. font-size: 14px;
  2808. color: #606266;
  2809. margin-bottom: 12px;
  2810. font-weight: 500;
  2811. }
  2812. .app-container :deep(.brand-tags-container) {
  2813. background: #fff;
  2814. border: 1px dashed #dcdfe6;
  2815. border-radius: 10px;
  2816. min-height: 64px;
  2817. padding: 12px;
  2818. }
  2819. .app-container :deep(.brand-tags-container .el-tag) {
  2820. border-radius: 8px;
  2821. }
  2822. .app-container :deep(.image-upload) {
  2823. display: inline-block;
  2824. margin-left: 10px;
  2825. }
  2826. .app-container :deep(.upload-placeholder) {
  2827. width: 100px;
  2828. height: 100px;
  2829. border: 1px dashed #d9d9d9;
  2830. border-radius: 4px;
  2831. display: flex;
  2832. align-items: center;
  2833. justify-content: center;
  2834. cursor: pointer;
  2835. color: #999;
  2836. font-size: 24px;
  2837. }
  2838. .app-container :deep(.upload-placeholder:hover) {
  2839. border-color: #409eff;
  2840. color: #409eff;
  2841. }
  2842. </style>