payment-form.vue 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067
  1. <!-- 付款申请流程专用表单组件 -->
  2. <template>
  3. <view class="payment-form-component">
  4. <!-- 基本信息 -->
  5. <uni-card>
  6. <uni-section titleFontSize="1.3rem" title="基本信息" type="line"></uni-section>
  7. <uni-forms ref="baseFormRef" :modelValue="baseForm" :rules="baseFormRules" label-position="left" :label-width="120" :border="true">
  8. <uni-forms-item name="department" label="申请部门">
  9. <uni-easyinput v-model="baseForm.department" disabled></uni-easyinput>
  10. </uni-forms-item>
  11. <uni-forms-item name="applyDate" label="申请日期">
  12. <uni-easyinput v-model="baseForm.applyDate" disabled></uni-easyinput>
  13. </uni-forms-item>
  14. <uni-forms-item name="contractNumber" label="合同">
  15. <view v-if="isInitiateOrFieldEditable('contract_number')" class="selector-wrapper" @click="openContractSelector">
  16. <text v-if="baseForm.contractNumber">{{ baseForm.contractNumber }}</text>
  17. <text v-else class="placeholder">选择合同</text>
  18. </view>
  19. <uni-easyinput v-else v-model="baseForm.contractNumber" disabled></uni-easyinput>
  20. <button v-if="isInitiateOrFieldEditable('contract_number') && baseForm.contractId" type="warn" size="mini" @click="clearContract" style="margin-left: 10px;">清除</button>
  21. </uni-forms-item>
  22. <uni-forms-item name="payerName" label="单位" required>
  23. <uni-easyinput v-if="isInitiateOrFieldEditable('payer_name')"
  24. v-model="baseForm.payerName"
  25. placeholder="请输入单位"></uni-easyinput>
  26. <uni-easyinput v-else v-model="baseForm.payerName" disabled></uni-easyinput>
  27. </uni-forms-item>
  28. <uni-forms-item name="payeeName" label="收款单位" required>
  29. <view v-if="isInitiateOrFieldEditable('payee_name') && !baseForm.contractId" class="payee-input-wrapper">
  30. <uni-easyinput
  31. v-model="baseForm.payeeName"
  32. placeholder="请输入或选择收款单位"
  33. @input="onPayeeInputChange"
  34. ></uni-easyinput>
  35. <button
  36. type="primary"
  37. size="mini"
  38. @click="openSupplierSelector"
  39. class="select-supplier-btn"
  40. >
  41. 选择供应商
  42. </button>
  43. </view>
  44. <uni-easyinput v-else v-model="baseForm.payeeName" disabled></uni-easyinput>
  45. </uni-forms-item>
  46. <uni-forms-item name="payeeBankAccount" label="开户行及账号" required>
  47. <uni-easyinput v-if="isInitiateOrFieldEditable('payee_bank_account')"
  48. v-model="baseForm.payeeBankAccount"
  49. placeholder="请输入开户行及账号"></uni-easyinput>
  50. <uni-easyinput v-else v-model="baseForm.payeeBankAccount" disabled></uni-easyinput>
  51. </uni-forms-item>
  52. <uni-forms-item name="paymentType" label="付款类型" required>
  53. <picker v-if="isInitiateOrFieldEditable('payment_type')"
  54. @change="onPaymentTypeChange"
  55. :value="paymentTypeIndex"
  56. :range="paymentTypeList"
  57. range-key="name">
  58. <view class="picker-wrapper">
  59. <text v-if="baseForm.paymentTypeName">{{ baseForm.paymentTypeName }}</text>
  60. <text v-else class="placeholder">请选择付款类型</text>
  61. </view>
  62. </picker>
  63. <uni-easyinput v-else v-model="baseForm.paymentTypeName" disabled></uni-easyinput>
  64. </uni-forms-item>
  65. <!-- 预付款:显示采购订单编号 -->
  66. <uni-forms-item v-if="baseForm.paymentType === '1'" name="purchaseOrderNo" label="采购订单编号">
  67. <view v-if="isInitiateOrFieldEditable('purchase_order_no')" class="selector-wrapper" @click="openPurchaseOrderSelector">
  68. <text v-if="baseForm.purchaseOrderNo">{{ baseForm.purchaseOrderNo }}</text>
  69. <text v-else class="placeholder">选择采购订单</text>
  70. </view>
  71. <uni-easyinput v-else v-model="baseForm.purchaseOrderNo" disabled></uni-easyinput>
  72. <button v-if="isInitiateOrFieldEditable('purchase_order_no') && baseForm.purchaseOrderNo" type="warn" size="mini" @click="clearPurchaseOrder" style="margin-left: 10px;">清除</button>
  73. </uni-forms-item>
  74. <!-- 到付款:显示入库单编号 -->
  75. <uni-forms-item v-if="baseForm.paymentType === '2'" name="storageOrderNo" label="入库单编号">
  76. <view v-if="isInitiateOrFieldEditable('storage_order_no')" class="selector-wrapper" @click="openStorageOrderSelector">
  77. <text v-if="baseForm.storageOrderNo">{{ baseForm.storageOrderNo }}</text>
  78. <text v-else class="placeholder">选择入库单</text>
  79. </view>
  80. <uni-easyinput v-else v-model="baseForm.storageOrderNo" disabled></uni-easyinput>
  81. <button v-if="isInitiateOrFieldEditable('storage_order_no') && baseForm.storageOrderNo" type="warn" size="mini" @click="clearStorageOrder" style="margin-left: 10px;">清除</button>
  82. </uni-forms-item>
  83. <uni-forms-item name="paymentMethod" label="付款方式" required>
  84. <uni-easyinput v-if="isInitiateOrFieldEditable('payment_method')"
  85. v-model="baseForm.paymentMethod"
  86. placeholder="请输入付款方式"></uni-easyinput>
  87. <uni-easyinput v-else v-model="baseForm.paymentMethod" disabled></uni-easyinput>
  88. </uni-forms-item>
  89. <uni-forms-item name="paymentDate" label="付款日期" required>
  90. <picker v-if="isInitiateOrFieldEditable('payment_date')"
  91. mode="date"
  92. :value="baseForm.paymentDate"
  93. @change="onPaymentDateChange">
  94. <view class="picker-wrapper">
  95. <text v-if="baseForm.paymentDate">{{ baseForm.paymentDate }}</text>
  96. <text v-else class="placeholder">请选择付款日期</text>
  97. </view>
  98. </picker>
  99. <uni-easyinput v-else v-model="baseForm.paymentDate" disabled></uni-easyinput>
  100. </uni-forms-item>
  101. <uni-forms-item name="amountUpper" label="付款金额(大写)">
  102. <uni-easyinput v-model="baseForm.amountUpper" disabled></uni-easyinput>
  103. </uni-forms-item>
  104. <uni-forms-item name="amountLower" label="付款金额(小写)" required>
  105. <uni-easyinput v-if="isInitiateOrFieldEditable('amount_lower')"
  106. v-model="baseForm.amountLower"
  107. placeholder="请输入付款金额"
  108. type="digit"
  109. @input="onAmountLowerInput"></uni-easyinput>
  110. <uni-easyinput v-else v-model="baseForm.amountLower" disabled></uni-easyinput>
  111. </uni-forms-item>
  112. <uni-forms-item name="purpose" label="用途或理由" required>
  113. <textarea v-if="isInitiateOrFieldEditable('purpose')"
  114. v-model="baseForm.purpose"
  115. placeholder="请输入用途或理由"
  116. class="textarea-input"></textarea>
  117. <view v-else class="textarea-display">{{ baseForm.purpose || '-' }}</view>
  118. </uni-forms-item>
  119. <uni-forms-item name="operator" label="经办人" required>
  120. <uni-easyinput v-model="baseForm.operator" disabled></uni-easyinput>
  121. </uni-forms-item>
  122. <!-- 部门负责人签名 -->
  123. <uni-forms-item label="部门负责人" name="deptApprover">
  124. <view class="element_value_container">
  125. <view v-if="isApprovalFieldEditable(deptApproverElem)" class="element_value">
  126. <view v-if="deptApproverElem && (!deptApproverElem.defaultValue || deptApproverElem.defaultValue == '')">
  127. <uni-row>
  128. <uni-col :span="24">
  129. <button type="primary" @click="handleApprovalSignature('dept_approver')">手动签名</button>
  130. </uni-col>
  131. <uni-col :span="24">
  132. <button style="margin-top: 5px;" type="primary" @click="handleAutoSeal('dept_approver')">一键签名</button>
  133. </uni-col>
  134. </uni-row>
  135. </view>
  136. <view v-else-if="deptApproverElem && deptApproverElem.defaultValue" class="signature_img">
  137. <img style="width: 100%;" mode="widthFix" @click="handleApprovalSignature('dept_approver')"
  138. :src="config.baseUrlPre + deptApproverElem.sealImgPath"
  139. :alt="deptApproverElem.elementName + '签名'" />
  140. </view>
  141. </view>
  142. <view v-else-if="deptApproverElem && typeof deptApproverElem.sealImgPath === 'string' && deptApproverElem.sealImgPath.startsWith('/shares')" class="signature_img">
  143. <img style="width: 100%;" mode="widthFix"
  144. :src="config.baseUrlPre + deptApproverElem.sealImgPath" />
  145. </view>
  146. </view>
  147. </uni-forms-item>
  148. <!-- 经办会计签名 -->
  149. <uni-forms-item label="经办会计" name="accountant">
  150. <view class="element_value_container">
  151. <view v-if="isApprovalFieldEditable(accountantElem)" class="element_value">
  152. <view v-if="accountantElem && (!accountantElem.defaultValue || accountantElem.defaultValue == '')">
  153. <uni-row>
  154. <uni-col :span="24">
  155. <button type="primary" @click="handleApprovalSignature('accountant')">手动签名</button>
  156. </uni-col>
  157. <uni-col :span="24">
  158. <button style="margin-top: 5px;" type="primary" @click="handleAutoSeal('accountant')">一键签名</button>
  159. </uni-col>
  160. </uni-row>
  161. </view>
  162. <view v-else-if="accountantElem && accountantElem.defaultValue" class="signature_img">
  163. <img style="width: 100%;" mode="widthFix" @click="handleApprovalSignature('accountant')"
  164. :src="config.baseUrlPre + accountantElem.sealImgPath"
  165. :alt="accountantElem.elementName + '签名'" />
  166. </view>
  167. </view>
  168. <view v-else-if="accountantElem && typeof accountantElem.sealImgPath === 'string' && accountantElem.sealImgPath.startsWith('/shares')" class="signature_img">
  169. <img style="width: 100%;" mode="widthFix"
  170. :src="config.baseUrlPre + accountantElem.sealImgPath" />
  171. </view>
  172. </view>
  173. </uni-forms-item>
  174. <!-- 财务经理签名 -->
  175. <uni-forms-item label="财务经理" name="financeManager">
  176. <view class="element_value_container">
  177. <view v-if="isApprovalFieldEditable(financeManagerElem)" class="element_value">
  178. <view v-if="financeManagerElem && (!financeManagerElem.defaultValue || financeManagerElem.defaultValue == '')">
  179. <uni-row>
  180. <uni-col :span="24">
  181. <button type="primary" @click="handleApprovalSignature('finance_manager')">手动签名</button>
  182. </uni-col>
  183. <uni-col :span="24">
  184. <button style="margin-top: 5px;" type="primary" @click="handleAutoSeal('finance_manager')">一键签名</button>
  185. </uni-col>
  186. </uni-row>
  187. </view>
  188. <view v-else-if="financeManagerElem && financeManagerElem.defaultValue" class="signature_img">
  189. <img style="width: 100%;" mode="widthFix" @click="handleApprovalSignature('finance_manager')"
  190. :src="config.baseUrlPre + financeManagerElem.sealImgPath"
  191. :alt="financeManagerElem.elementName + '签名'" />
  192. </view>
  193. </view>
  194. <view v-else-if="financeManagerElem && typeof financeManagerElem.sealImgPath === 'string' && financeManagerElem.sealImgPath.startsWith('/shares')" class="signature_img">
  195. <img style="width: 100%;" mode="widthFix"
  196. :src="config.baseUrlPre + financeManagerElem.sealImgPath" />
  197. </view>
  198. </view>
  199. </uni-forms-item>
  200. <!-- 分管副总部门签名 -->
  201. <uni-forms-item label="分管副总(部门)" name="deputyManagerDept">
  202. <view class="element_value_container">
  203. <view v-if="isApprovalFieldEditable(deputyManagerDeptElem)" class="element_value">
  204. <view v-if="deputyManagerDeptElem && (!deputyManagerDeptElem.defaultValue || deputyManagerDeptElem.defaultValue == '')">
  205. <uni-row>
  206. <uni-col :span="24">
  207. <button type="primary" @click="handleApprovalSignature('deputy_manager_dept')">手动签名</button>
  208. </uni-col>
  209. <uni-col :span="24">
  210. <button style="margin-top: 5px;" type="primary" @click="handleAutoSeal('deputy_manager_dept')">一键签名</button>
  211. </uni-col>
  212. </uni-row>
  213. </view>
  214. <view v-else-if="deputyManagerDeptElem && deputyManagerDeptElem.defaultValue" class="signature_img">
  215. <img style="width: 100%;" mode="widthFix" @click="handleApprovalSignature('deputy_manager_dept')"
  216. :src="config.baseUrlPre + deputyManagerDeptElem.sealImgPath"
  217. :alt="deputyManagerDeptElem.elementName + '签名'" />
  218. </view>
  219. </view>
  220. <view v-else-if="deputyManagerDeptElem && typeof deputyManagerDeptElem.sealImgPath === 'string' && deputyManagerDeptElem.sealImgPath.startsWith('/shares')" class="signature_img">
  221. <img style="width: 100%;" mode="widthFix"
  222. :src="config.baseUrlPre + deputyManagerDeptElem.sealImgPath" />
  223. </view>
  224. </view>
  225. </uni-forms-item>
  226. <!-- 分管副总审计签名 -->
  227. <uni-forms-item label="分管副总(审计)" name="deputyManagerAudit">
  228. <view class="element_value_container">
  229. <view v-if="isApprovalFieldEditable(deputyManagerAuditElem)" class="element_value">
  230. <view v-if="deputyManagerAuditElem && (!deputyManagerAuditElem.defaultValue || deputyManagerAuditElem.defaultValue == '')">
  231. <uni-row>
  232. <uni-col :span="24">
  233. <button type="primary" @click="handleApprovalSignature('deputy_manager_audit')">手动签名</button>
  234. </uni-col>
  235. <uni-col :span="24">
  236. <button style="margin-top: 5px;" type="primary" @click="handleAutoSeal('deputy_manager_audit')">一键签名</button>
  237. </uni-col>
  238. </uni-row>
  239. </view>
  240. <view v-else-if="deputyManagerAuditElem && deputyManagerAuditElem.defaultValue" class="signature_img">
  241. <img style="width: 100%;" mode="widthFix" @click="handleApprovalSignature('deputy_manager_audit')"
  242. :src="config.baseUrlPre + deputyManagerAuditElem.sealImgPath"
  243. :alt="deputyManagerAuditElem.elementName + '签名'" />
  244. </view>
  245. </view>
  246. <view v-else-if="deputyManagerAuditElem && typeof deputyManagerAuditElem.sealImgPath === 'string' && deputyManagerAuditElem.sealImgPath.startsWith('/shares')" class="signature_img">
  247. <img style="width: 100%;" mode="widthFix"
  248. :src="config.baseUrlPre + deputyManagerAuditElem.sealImgPath" />
  249. </view>
  250. </view>
  251. </uni-forms-item>
  252. <!-- 公司总经理签名 -->
  253. <uni-forms-item label="公司总经理" name="generalManager">
  254. <view class="element_value_container">
  255. <view v-if="isApprovalFieldEditable(generalManagerElem)" class="element_value">
  256. <view v-if="generalManagerElem && (!generalManagerElem.defaultValue || generalManagerElem.defaultValue == '')">
  257. <uni-row>
  258. <uni-col :span="24">
  259. <button type="primary" @click="handleApprovalSignature('general_manager')">手动签名</button>
  260. </uni-col>
  261. <uni-col :span="24">
  262. <button style="margin-top: 5px;" type="primary" @click="handleAutoSeal('general_manager')">一键签名</button>
  263. </uni-col>
  264. </uni-row>
  265. </view>
  266. <view v-else-if="generalManagerElem && generalManagerElem.defaultValue" class="signature_img">
  267. <img style="width: 100%;" mode="widthFix" @click="handleApprovalSignature('general_manager')"
  268. :src="config.baseUrlPre + generalManagerElem.sealImgPath"
  269. :alt="generalManagerElem.elementName + '签名'" />
  270. </view>
  271. </view>
  272. <view v-else-if="generalManagerElem && typeof generalManagerElem.sealImgPath === 'string' && generalManagerElem.sealImgPath.startsWith('/shares')" class="signature_img">
  273. <img style="width: 100%;" mode="widthFix"
  274. :src="config.baseUrlPre + generalManagerElem.sealImgPath" />
  275. </view>
  276. </view>
  277. </uni-forms-item>
  278. <!-- 董事长签名 -->
  279. <uni-forms-item label="董事长" name="chairman">
  280. <view class="element_value_container">
  281. <view v-if="isApprovalFieldEditable(chairmanElem)" class="element_value">
  282. <view v-if="chairmanElem && (!chairmanElem.defaultValue || chairmanElem.defaultValue == '')">
  283. <uni-row>
  284. <uni-col :span="24">
  285. <button type="primary" @click="handleApprovalSignature('chairman')">手动签名</button>
  286. </uni-col>
  287. <uni-col :span="24">
  288. <button style="margin-top: 5px;" type="primary" @click="handleAutoSeal('chairman')">一键签名</button>
  289. </uni-col>
  290. </uni-row>
  291. </view>
  292. <view v-else-if="chairmanElem && chairmanElem.defaultValue" class="signature_img">
  293. <img style="width: 100%;" mode="widthFix" @click="handleApprovalSignature('chairman')"
  294. :src="config.baseUrlPre + chairmanElem.sealImgPath"
  295. :alt="chairmanElem.elementName + '签名'" />
  296. </view>
  297. </view>
  298. <view v-else-if="chairmanElem && typeof chairmanElem.sealImgPath === 'string' && chairmanElem.sealImgPath.startsWith('/shares')" class="signature_img">
  299. <img style="width: 100%;" mode="widthFix"
  300. :src="config.baseUrlPre + chairmanElem.sealImgPath" />
  301. </view>
  302. </view>
  303. </uni-forms-item>
  304. </uni-forms>
  305. </uni-card>
  306. <!-- 签名板弹出层 -->
  307. <uni-popup ref="signaturePopup" @maskClick="closeSignature">
  308. <view class="signature_container" :class="{ 'signature_container_landscape': isLandscape }">
  309. <view class="signature_content">
  310. <l-signature ref="signatureRef" v-if="signaturePopupShow" :landscape="isLandscape" :penSize="8"
  311. :minLineWidth="4" :maxLineWidth="12" :openSmooth="true" :preferToDataURL="true"
  312. backgroundColor="#ffffff" penColor="black"></l-signature>
  313. </view>
  314. <view class="signature_button_container">
  315. <uni-row :gutter="10">
  316. <uni-col :span="6">
  317. <button type="warn" @click="onclickSignatureButton('undo')">撤销</button>
  318. </uni-col>
  319. <uni-col :span="6">
  320. <button type="warn" @click="onclickSignatureButton('clear')">清空</button>
  321. </uni-col>
  322. <uni-col :span="6">
  323. <button type="primary" @click="onclickSignatureButton('save')">保存</button>
  324. </uni-col>
  325. <uni-col :span="6">
  326. <button @click="onclickSignatureButton('landscape')">全屏</button>
  327. </uni-col>
  328. </uni-row>
  329. </view>
  330. </view>
  331. </uni-popup>
  332. <!-- 选择器弹出层(供应商/合同/采购订单/入库单) -->
  333. <uni-popup ref="selectorPopup" type="center">
  334. <view class="selector-popup">
  335. <view class="popup-header">
  336. <text class="popup-title">{{ selectorTitle }}</text>
  337. <uni-icons type="closeempty" size="20" @click="closePopup"></uni-icons>
  338. </view>
  339. <!-- 选择器内容 -->
  340. <view class="popup-content-wrapper">
  341. <!-- 搜索栏 -->
  342. <view class="search-bar">
  343. <uni-easyinput
  344. v-model="searchKeyword"
  345. :placeholder="searchPlaceholder"
  346. clearable
  347. @confirm="handleSearch"
  348. />
  349. <button type="primary" size="mini" @click="handleSearch">搜索</button>
  350. </view>
  351. <!-- 合同类型筛选 -->
  352. <view v-if="selectorType === 'contract'" class="filter-bar">
  353. <picker @change="onContractTypeChange" :range="contractTypeList" range-key="name">
  354. <view class="picker-filter">
  355. {{ selectedContractTypeName || '全部合同类型' }}
  356. </view>
  357. </picker>
  358. </view>
  359. <!-- 入库单多选提示 -->
  360. <view v-if="selectorType === 'storageOrder' && selectedStorageOrders.length > 0" class="selected-tip">
  361. 已选择 {{ selectedStorageOrders.length }} 个入库单
  362. </view>
  363. <scroll-view
  364. scroll-y
  365. class="popup-content"
  366. refresher-enabled
  367. :refresher-triggered="isRefreshing"
  368. @refresherrefresh="onRefresh"
  369. @scrolltolower="loadMore"
  370. >
  371. <view v-for="(item, index) in selectorList" :key="index"
  372. class="selector-item"
  373. :class="{ 'selected': isStorageOrderSelected(item) }"
  374. @click="toggleStorageOrderSelection(item)">
  375. <view class="selector-item-content">
  376. <!-- 入库单显示复选框 -->
  377. <view v-if="selectorType === 'storageOrder'" class="checkbox-wrapper">
  378. <text class="checkbox-icon">{{ isStorageOrderSelected(item) ? '✓' : '' }}</text>
  379. </view>
  380. <view class="item-info">
  381. <!-- 名称(合同名称/采购订单名称/入库单名称) -->
  382. <text class="item-name">{{ getSelectorItemName(item) }}</text>
  383. <!-- 编号(合同编号/采购订单编号/入库单编号) -->
  384. <text v-if="getItemCode(item)" class="item-code">{{ getItemCode(item) }}</text>
  385. <!-- 供应商(采购订单/入库单) -->
  386. <text v-if="getItemVendor(item)" class="item-vendor">{{ getItemVendor(item) }}</text>
  387. <!-- 总金额(采购订单/入库单) -->
  388. <text v-if="getItemAmount(item)" class="item-amount">金额:¥{{ getItemAmount(item) }}</text>
  389. <!-- 单据状态(采购订单/入库单) -->
  390. <text v-if="getItemStatus(item)" class="item-status" :class="getStatusClass(item)">{{ getItemStatus(item) }}</text>
  391. <!-- 创建时间(采购订单/入库单) -->
  392. <text v-if="getItemCreateTime(item)" class="item-time">{{ getItemCreateTime(item) }}</text>
  393. <!-- 合同类型显示(仅合同) -->
  394. <text v-if="selectorType === 'contract' && getContractTypeName(item)" class="item-type">
  395. 【{{ getContractTypeName(item) }}】
  396. </text>
  397. </view>
  398. </view>
  399. </view>
  400. <view v-if="isLoading && selectorList.length > 0" class="loading-text">
  401. <uni-load-more status="loading" />
  402. </view>
  403. <view v-else-if="!hasMore && selectorList.length > 0" class="no-more-text">
  404. <text>没有更多了</text>
  405. </view>
  406. <view v-if="selectorList.length === 0 && !isLoading" class="empty-data">
  407. <text>{{ searchKeyword ? '暂无相关数据' : '暂无数据' }}</text>
  408. </view>
  409. </scroll-view>
  410. </view>
  411. <!-- 入库单确认按钮(固定在底部) -->
  412. <view v-if="selectorType === 'storageOrder'" class="popup-footer-fixed">
  413. <button class="cancel-btn" @click="closePopup">取消</button>
  414. <button class="confirm-btn" @click="confirmStorageOrderSelection">确定</button>
  415. </view>
  416. </view>
  417. </uni-popup>
  418. </view>
  419. </template>
  420. <script setup lang="ts">
  421. import { ref, watch, computed, nextTick, onMounted } from 'vue'
  422. import { useUserStore } from '@/store/user.js'
  423. import config from '@/config.js'
  424. import { uploadSignatureBoardImg, getSeal } from '@/api/process.js'
  425. import { getSupplierList, getContractListForPayment, getPurchaseOrderList, getStorageOrderList } from '@/api/payment.js'
  426. import $modal from '@/plugins/modal.js'
  427. const userStore = useUserStore()
  428. const props = defineProps({
  429. formData: {
  430. type: Object,
  431. default: () => ({})
  432. },
  433. formElements: {
  434. type: Array,
  435. default: () => []
  436. },
  437. repeatingForm: {
  438. type: Object,
  439. default: () => ({ elementItem: [], elements: [] })
  440. },
  441. isInitiate: {
  442. type: Boolean,
  443. default: false
  444. },
  445. editableFields: {
  446. type: Array,
  447. default: () => []
  448. },
  449. currentTacheOpinion: {
  450. type: Object,
  451. default: null
  452. }
  453. })
  454. const emits = defineEmits(['update', 'signature-change'])
  455. // 表单数据
  456. const baseForm = ref<any>({
  457. payerName: '',
  458. payeeName: '',
  459. vendorCode: '',
  460. contractId: '',
  461. contractNumber: '',
  462. contractName: '',
  463. purchaseOrderNo: '',
  464. storageOrderNo: '',
  465. orderIdFromMes: '',
  466. amountLower: '',
  467. amountUpper: '',
  468. paymentType: '',
  469. paymentTypeName: '',
  470. paymentMethod: '',
  471. paymentDate: '',
  472. purpose: '',
  473. payeeBankAccount: '',
  474. bankName: '',
  475. bankAccount: '',
  476. accountId: '',
  477. accountName: '',
  478. applyDate: '',
  479. initiator: '',
  480. department: '',
  481. depid: null,
  482. operator: '',
  483. operatorId: ''
  484. })
  485. const baseFormRules = ref({
  486. payerName: [
  487. {
  488. required: true,
  489. errorMessage: '请输入单位'
  490. }
  491. ],
  492. payeeName: [
  493. {
  494. required: true,
  495. errorMessage: '请选择收款单位'
  496. }
  497. ],
  498. amountLower: [
  499. {
  500. required: true,
  501. errorMessage: '请输入付款金额'
  502. }
  503. ],
  504. paymentType: [
  505. {
  506. required: true,
  507. errorMessage: '请选择付款类型'
  508. }
  509. ],
  510. paymentMethod: [
  511. {
  512. required: true,
  513. errorMessage: '请输入付款方式'
  514. }
  515. ],
  516. paymentDate: [
  517. {
  518. required: true,
  519. errorMessage: '请选择付款日期'
  520. }
  521. ],
  522. purpose: [
  523. {
  524. required: true,
  525. errorMessage: '请输入用途或理由'
  526. }
  527. ],
  528. payeeBankAccount: [
  529. {
  530. required: true,
  531. errorMessage: '请输入开户行及账号'
  532. }
  533. ],
  534. operator: [
  535. {
  536. required: true,
  537. errorMessage: '请选择经办人'
  538. }
  539. ]
  540. })
  541. const signaturePopup = ref(null)
  542. const signatureRef = ref(null)
  543. const signatureImg = ref('')
  544. const currentApprovalText = ref('')
  545. const approvals = ref<any[]>([])
  546. const signaturePopupShow = ref(false)
  547. const isLandscape = ref(false)
  548. // 获取指定的审批意见元素
  549. const getApprovalElement = (fieldName: string) => {
  550. if (!props.formElements || !Array.isArray(props.formElements)) {
  551. return null
  552. }
  553. // 从 formElements 中找到对应的字段
  554. const elem = props.formElements.find(e => e.tableField === fieldName || e.elementName.includes(fieldName.replace(/_/g, '')))
  555. return elem || null
  556. }
  557. const deptApproverElem = computed(() => getApprovalElement('dept_approver'))
  558. const accountantElem = computed(() => getApprovalElement('accountant'))
  559. const financeManagerElem = computed(() => getApprovalElement('finance_manager'))
  560. const deputyManagerDeptElem = computed(() => getApprovalElement('deputy_manager_dept'))
  561. const deputyManagerAuditElem = computed(() => getApprovalElement('deputy_manager_audit'))
  562. const generalManagerElem = computed(() => getApprovalElement('general_manager'))
  563. const chairmanElem = computed(() => getApprovalElement('chairman'))
  564. const isSeModel = computed(() => props.isInitiate)
  565. const isApprovalFieldEditable = (elem: any) => {
  566. if (!elem) return false
  567. return props.editableFields && props.editableFields.includes(elem.tableField)
  568. }
  569. let lastFormInsId = ''
  570. const getFieldEditable = (fieldName: string) => {
  571. if (!props.editableFields || props.editableFields.length === 0) {
  572. return false
  573. }
  574. return props.editableFields.includes(fieldName)
  575. }
  576. const isInitiateOrFieldEditable = (fieldName: string) => {
  577. return props.isInitiate || getFieldEditable(fieldName)
  578. }
  579. // 监听表单数据变化
  580. watch(() => props.formData, (newVal) => {
  581. if (!newVal || Object.keys(newVal).length === 0) {
  582. return
  583. }
  584. const currentFormInsId = newVal.lFormInsId || newVal.universalid || ''
  585. if (currentFormInsId && currentFormInsId !== lastFormInsId) {
  586. lastFormInsId = currentFormInsId
  587. baseForm.value = {
  588. payerName: newVal.payerName || '',
  589. payeeName: newVal.payeeName || '',
  590. vendorCode: newVal.vendorCode || '',
  591. contractId: newVal.contractId || '',
  592. contractNumber: newVal.contractNumber || '',
  593. contractName: newVal.contractName || '',
  594. purchaseOrderNo: newVal.purchaseOrderNo || '',
  595. storageOrderNo: newVal.storageOrderNo || '',
  596. orderIdFromMes: newVal.orderIdFromMes || '',
  597. amountLower: (newVal.amountLower !== undefined && newVal.amountLower !== null) ? String(newVal.amountLower) : '',
  598. amountUpper: newVal.amountUpper || '',
  599. paymentType: newVal.paymentType || '',
  600. paymentTypeName: getPaymentTypeName(newVal.paymentType),
  601. paymentMethod: newVal.paymentMethod || '',
  602. paymentDate: newVal.paymentDate || '',
  603. purpose: newVal.purpose || '',
  604. payeeBankAccount: newVal.payeeBankAccount || '',
  605. bankName: newVal.bankName || '',
  606. bankAccount: newVal.bankAccount || '',
  607. accountId: newVal.accountId || '',
  608. accountName: newVal.accountName || '',
  609. applyDate: newVal.applyDate || '',
  610. initiator: newVal.initiator || '',
  611. department: newVal.department || '',
  612. depid: newVal.depid || null,
  613. operator: newVal.operator || '',
  614. operatorId: newVal.operatorId || ''
  615. }
  616. }
  617. }, { immediate: true, deep: true })
  618. // 监听收款单位变化,如果手动修改则清空关联信息
  619. let lastPayeeName = ''
  620. watch(() => baseForm.value.payeeName, (newVal, oldVal) => {
  621. // 只在发起或字段可编辑时生效
  622. if (!isInitiateOrFieldEditable('payee_name')) return
  623. // 如果有合同ID,不允许手动修改
  624. if (baseForm.value.contractId) return
  625. // 如果是从有值变为空,不处理
  626. if (!newVal && oldVal) return
  627. // 如果是通过选择器选择的(有vendorCode),不处理
  628. if (baseForm.value.vendorCode) return
  629. // 手动输入时,清空所有关联信息和供应商编码
  630. clearRelatedOrders()
  631. baseForm.value.vendorCode = ''
  632. })
  633. // 收款单位输入处理
  634. function onPayeeInputChange(value: string) {
  635. // 如果有合同ID,不允许手动修改
  636. if (baseForm.value.contractId) return
  637. // 手动输入时,立即清空所有关联信息和供应商编码
  638. clearRelatedOrders()
  639. baseForm.value.vendorCode = ''
  640. }
  641. function getPaymentTypeName(type: string): string {
  642. const typeMap: any = {
  643. '1': '预付款',
  644. '2': '到付款'
  645. }
  646. return typeMap[type] || type || '-'
  647. }
  648. // 打开合同选择器
  649. async function openContractSelector() {
  650. if (!isInitiateOrFieldEditable('contract_number')) return
  651. // 检查是否已选择采购订单或入库单
  652. if (baseForm.value.purchaseOrderNo || baseForm.value.storageOrderNo) {
  653. $modal.msg('已选择采购单或入库单,不能同时选择合同!请先清除。')
  654. return
  655. }
  656. selectorType.value = 'contract'
  657. selectedContractType.value = '' // 重置合同类型筛选
  658. searchKeyword.value = ''
  659. currentPage.value = 1
  660. hasMore.value = true
  661. selectorList.value = []
  662. await loadContracts(1, false)
  663. selectorPopup.value.open()
  664. }
  665. // 打开采购订单选择器
  666. async function openPurchaseOrderSelector() {
  667. if (!isInitiateOrFieldEditable('purchase_order_no')) return
  668. // 检查是否已选择合同或入库单
  669. if (baseForm.value.contractId || baseForm.value.storageOrderNo) {
  670. $modal.msg('已选择合同或入库单,不能同时选择采购订单!请先清除。')
  671. return
  672. }
  673. if (!baseForm.value.vendorCode) {
  674. $modal.msg('请先选择收款单位(供应商)!')
  675. return
  676. }
  677. selectorType.value = 'purchaseOrder'
  678. searchKeyword.value = ''
  679. currentPage.value = 1
  680. hasMore.value = true
  681. selectorList.value = []
  682. await loadPurchaseOrders(1, false)
  683. selectorPopup.value.open()
  684. }
  685. // 打开入库单选择器
  686. async function openStorageOrderSelector() {
  687. if (!isInitiateOrFieldEditable('storage_order_no')) return
  688. // 检查是否已选择合同或采购订单
  689. if (baseForm.value.contractId || baseForm.value.purchaseOrderNo) {
  690. $modal.msg('已选择合同或采购订单,不能同时选择入库单!请先清除。')
  691. return
  692. }
  693. if (!baseForm.value.vendorCode) {
  694. $modal.msg('请先选择收款单位(供应商)!')
  695. return
  696. }
  697. selectorType.value = 'storageOrder'
  698. selectedStorageOrders.value = [] // 重置多选列表
  699. searchKeyword.value = ''
  700. currentPage.value = 1
  701. hasMore.value = true
  702. selectorList.value = []
  703. await loadStorageOrders(1, false)
  704. selectorPopup.value.open()
  705. }
  706. // 打开经办人选择器
  707. async function openOperatorSelector() {
  708. if (!isInitiateOrFieldEditable('operator')) return
  709. $modal.msg('人员选择功能待实现')
  710. }
  711. // 付款类型变化
  712. function onPaymentTypeChange(e: any) {
  713. const index = e.detail.value
  714. baseForm.value.paymentType = paymentTypeList.value[index].value
  715. baseForm.value.paymentTypeName = paymentTypeList.value[index].name
  716. // 根据付款类型切换显示字段并清空被隐藏的字段
  717. togglePaymentFields(baseForm.value.paymentType)
  718. }
  719. // 根据付款类型切换显示字段
  720. function togglePaymentFields(paymentType: string) {
  721. if (paymentType === '1') { // 预付款
  722. // 清空入库单编号
  723. baseForm.value.storageOrderNo = ''
  724. baseForm.value.orderIdFromMes = ''
  725. } else if (paymentType === '2') { // 到付款
  726. // 清空采购订单编号
  727. baseForm.value.purchaseOrderNo = ''
  728. baseForm.value.orderIdFromMes = ''
  729. }
  730. }
  731. // 付款日期变化
  732. function onPaymentDateChange(e: any) {
  733. baseForm.value.paymentDate = e.detail.value
  734. }
  735. // 金额小写输入
  736. function onAmountLowerInput(value: string) {
  737. // 限制只能输入数字和小数点(参考PC端restrictToNumber函数)
  738. const restricted = restrictToNumber(value)
  739. // 如果值被修改了,更新表单值
  740. if (restricted !== value) {
  741. baseForm.value.amountLower = restricted
  742. }
  743. // 转换为大写
  744. convertToChinese()
  745. }
  746. // 限制输入框只能输入数字和小数点(参考PC端)
  747. function restrictToNumber(value: string): string {
  748. if (!value) return value
  749. // 只允许数字、小数点,且小数点只能有一个
  750. let newValue = value.replace(/[^0-9.]/g, '')
  751. // 确保小数点不超过一个
  752. const dotIndex = newValue.indexOf('.')
  753. if (dotIndex !== -1) {
  754. newValue = newValue.substring(0, dotIndex + 1) + newValue.substring(dotIndex + 1).replace(/\./g, '')
  755. }
  756. // 如果第一个字符是小数点,则在前面加0
  757. if (newValue.charAt(0) === '.') {
  758. newValue = '0' + newValue
  759. }
  760. return newValue
  761. }
  762. // 金额转大写
  763. function convertToChinese() {
  764. // 先清理输入值
  765. if (baseForm.value.amountLower) {
  766. baseForm.value.amountLower = restrictToNumber(baseForm.value.amountLower)
  767. }
  768. const amount = parseFloat(baseForm.value.amountLower)
  769. if (isNaN(amount) || amount <= 0) {
  770. baseForm.value.amountUpper = ''
  771. return
  772. }
  773. const cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
  774. const cnIntRadice = ['', '拾', '佰', '仟']
  775. const cnIntUnits = ['', '万', '亿', '兆']
  776. const cnDecUnits = ['角', '分']
  777. const cnInteger = '整'
  778. const cnIntLast = '元'
  779. let integerNum = Math.floor(amount)
  780. let decimalNum = Math.round((amount - integerNum) * 100)
  781. let chineseStr = ''
  782. if (integerNum === 0) {
  783. chineseStr = cnNums[0] + cnIntLast
  784. } else {
  785. let intLen = integerNum.toString().length
  786. let zeroCount = 0
  787. for (let i = 0; i < intLen; i++) {
  788. let n = integerNum.toString().charAt(i)
  789. let p = intLen - i - 1
  790. let q = Math.floor(p / 4)
  791. let m = p % 4
  792. if (n === '0') {
  793. zeroCount++
  794. } else {
  795. if (zeroCount > 0) {
  796. chineseStr += cnNums[0]
  797. }
  798. zeroCount = 0
  799. chineseStr += cnNums[parseInt(n)] + cnIntRadice[m]
  800. }
  801. if (m === 0 && zeroCount < 4) {
  802. chineseStr += cnIntUnits[q]
  803. }
  804. }
  805. chineseStr += cnIntLast
  806. }
  807. if (decimalNum !== 0) {
  808. let jiao = Math.floor(decimalNum / 10)
  809. let fen = decimalNum % 10
  810. if (jiao !== 0) {
  811. chineseStr += cnNums[jiao] + cnDecUnits[0]
  812. }
  813. if (fen !== 0) {
  814. chineseStr += cnNums[fen] + cnDecUnits[1]
  815. }
  816. } else {
  817. chineseStr += cnInteger
  818. }
  819. baseForm.value.amountUpper = chineseStr
  820. }
  821. // 打开供应商选择器
  822. async function openSupplierSelector() {
  823. if (baseForm.value.contractId) {
  824. $modal.msg('已选择合同,收款单位由合同自动填充,如需修改请先清除合同!')
  825. return
  826. }
  827. selectorType.value = 'supplier'
  828. selectorList.value = []
  829. searchKeyword.value = ''
  830. await loadSuppliers(1, false)
  831. selectorPopup.value.open()
  832. }
  833. // 加载供应商列表
  834. async function loadSuppliers(page: number, append: boolean = false) {
  835. if (isLoading.value) return
  836. isLoading.value = true
  837. if (page === 1) {
  838. isRefreshing.value = true
  839. }
  840. try {
  841. const res = await getSupplierList(
  842. userStore.user.useId,
  843. page,
  844. pageSize,
  845. searchKeyword.value
  846. )
  847. if (res.returnCode === '1') {
  848. const result = res.returnParams
  849. const newList = result.list || []
  850. if (append) {
  851. selectorList.value = [...selectorList.value, ...newList]
  852. } else {
  853. selectorList.value = newList
  854. }
  855. hasMore.value = selectorList.value.length < result.total
  856. }
  857. } catch (error) {
  858. console.error('加载供应商失败', error)
  859. } finally {
  860. isLoading.value = false
  861. isRefreshing.value = false
  862. }
  863. }
  864. // 加载合同列表
  865. async function loadContracts(page: number, append: boolean = false) {
  866. if (isLoading.value) return
  867. isLoading.value = true
  868. if (page === 1) {
  869. isRefreshing.value = true
  870. }
  871. try {
  872. const res = await getContractListForPayment(
  873. userStore.user.useId,
  874. page,
  875. pageSize,
  876. searchKeyword.value,
  877. '', // supplierCode 不传,由后端根据其他条件筛选
  878. selectedContractType.value // 合同类型筛选
  879. )
  880. if (res.returnCode === '1') {
  881. const result = res.returnParams
  882. const newList = result.list || []
  883. if (append) {
  884. selectorList.value = [...selectorList.value, ...newList]
  885. } else {
  886. selectorList.value = newList
  887. }
  888. hasMore.value = selectorList.value.length < result.total
  889. }
  890. } catch (error) {
  891. console.error('加载合同失败', error)
  892. } finally {
  893. isLoading.value = false
  894. isRefreshing.value = false
  895. }
  896. }
  897. // 加载采购订单列表
  898. async function loadPurchaseOrders(page: number, append: boolean = false) {
  899. if (isLoading.value) return
  900. isLoading.value = true
  901. if (page === 1) {
  902. isRefreshing.value = true
  903. }
  904. try {
  905. const res = await getPurchaseOrderList(
  906. userStore.user.useId,
  907. page,
  908. pageSize,
  909. baseForm.value.vendorCode
  910. )
  911. if (res.returnCode === '1') {
  912. const result = res.returnParams
  913. const newList = result.list || []
  914. if (append) {
  915. selectorList.value = [...selectorList.value, ...newList]
  916. } else {
  917. selectorList.value = newList
  918. }
  919. hasMore.value = selectorList.value.length < result.total
  920. }
  921. } catch (error) {
  922. console.error('加载采购订单失败', error)
  923. } finally {
  924. isLoading.value = false
  925. isRefreshing.value = false
  926. }
  927. }
  928. // 加载入库单列表
  929. async function loadStorageOrders(page: number, append: boolean = false) {
  930. if (isLoading.value) return
  931. isLoading.value = true
  932. if (page === 1) {
  933. isRefreshing.value = true
  934. }
  935. try {
  936. const res = await getStorageOrderList(
  937. userStore.user.useId,
  938. page,
  939. pageSize,
  940. baseForm.value.vendorCode
  941. )
  942. if (res.returnCode === '1') {
  943. const result = res.returnParams
  944. const newList = result.list || []
  945. if (append) {
  946. selectorList.value = [...selectorList.value, ...newList]
  947. } else {
  948. selectorList.value = newList
  949. }
  950. hasMore.value = selectorList.value.length < result.total
  951. }
  952. } catch (error) {
  953. console.error('加载入库单失败', error)
  954. } finally {
  955. isLoading.value = false
  956. isRefreshing.value = false
  957. }
  958. }
  959. // 付款类型列表
  960. const paymentTypeList = ref([
  961. { value: '1', name: '预付款' },
  962. { value: '2', name: '到付款' }
  963. ])
  964. const paymentTypeIndex = computed(() => {
  965. const index = paymentTypeList.value.findIndex(item => item.value === baseForm.value.paymentType)
  966. return index >= 0 ? index : 0
  967. })
  968. // 选择器相关
  969. const selectorPopup = ref(null)
  970. const selectorType = ref('supplier') // supplier, contract, purchaseOrder, storageOrder
  971. const selectorList = ref<any[]>([])
  972. const currentPage = ref(1)
  973. const pageSize = 20
  974. const hasMore = ref(true)
  975. const isLoading = ref(false)
  976. const isRefreshing = ref(false)
  977. const searchKeyword = ref('')
  978. // 入库单多选相关
  979. const selectedStorageOrders = ref<any[]>([])
  980. // 合同类型相关
  981. const contractTypeList = ref([
  982. { value: '', name: '全部' },
  983. { value: '1', name: '销售合同' },
  984. { value: '2', name: '采购合同' },
  985. { value: '3', name: '技术服务合同' }
  986. ])
  987. const selectedContractType = ref('')
  988. const selectedContractTypeName = computed(() => {
  989. if (!selectedContractType.value) return ''
  990. const type = contractTypeList.value.find(t => t.value === selectedContractType.value)
  991. return type ? type.name : ''
  992. })
  993. // 选择器标题
  994. const selectorTitle = computed(() => {
  995. const titles: Record<string, string> = {
  996. supplier: '选择供应商',
  997. contract: '选择合同',
  998. purchaseOrder: '选择采购订单',
  999. storageOrder: '选择入库单'
  1000. }
  1001. return titles[selectorType.value] || '选择'
  1002. })
  1003. // 搜索框提示
  1004. const searchPlaceholder = computed(() => {
  1005. const placeholders: Record<string, string> = {
  1006. supplier: '输入供应商名称搜索',
  1007. contract: '输入合同名称搜索',
  1008. purchaseOrder: '输入采购订单编号搜索',
  1009. storageOrder: '输入入库单编号搜索'
  1010. }
  1011. return placeholders[selectorType.value] || '请输入搜索内容'
  1012. })
  1013. function closePopup() {
  1014. selectorPopup.value.close()
  1015. }
  1016. function handleSearch() {
  1017. currentPage.value = 1
  1018. if (selectorType.value === 'supplier') {
  1019. loadSuppliers(1, false)
  1020. } else if (selectorType.value === 'contract') {
  1021. loadContracts(1, false)
  1022. } else if (selectorType.value === 'purchaseOrder') {
  1023. loadPurchaseOrders(1, false)
  1024. } else if (selectorType.value === 'storageOrder') {
  1025. loadStorageOrders(1, false)
  1026. }
  1027. }
  1028. function onRefresh() {
  1029. currentPage.value = 1
  1030. if (selectorType.value === 'supplier') {
  1031. loadSuppliers(1, false)
  1032. } else if (selectorType.value === 'contract') {
  1033. loadContracts(1, false)
  1034. } else if (selectorType.value === 'purchaseOrder') {
  1035. loadPurchaseOrders(1, false)
  1036. } else if (selectorType.value === 'storageOrder') {
  1037. loadStorageOrders(1, false)
  1038. }
  1039. }
  1040. function loadMore() {
  1041. if (!hasMore.value || isLoading.value) return
  1042. currentPage.value++
  1043. if (selectorType.value === 'supplier') {
  1044. loadSuppliers(currentPage.value, true)
  1045. } else if (selectorType.value === 'contract') {
  1046. loadContracts(currentPage.value, true)
  1047. } else if (selectorType.value === 'purchaseOrder') {
  1048. loadPurchaseOrders(currentPage.value, true)
  1049. } else if (selectorType.value === 'storageOrder') {
  1050. loadStorageOrders(currentPage.value, true)
  1051. }
  1052. }
  1053. // 合同类型变化
  1054. function onContractTypeChange(e: any) {
  1055. const selectedIndex = e.detail.value
  1056. selectedContractType.value = contractTypeList.value[selectedIndex].value
  1057. // 重新加载合同列表
  1058. loadContracts(1, false)
  1059. }
  1060. // 获取选择器项名称
  1061. function getSelectorItemName(item: any): string {
  1062. if (selectorType.value === 'supplier') {
  1063. return item.vendorName || item.name || ''
  1064. } else if (selectorType.value === 'contract') {
  1065. return item.contract_name || item.contractName || ''
  1066. } else if (selectorType.value === 'purchaseOrder') {
  1067. return item.purchaseCode || item.purchase_code || ''
  1068. } else if (selectorType.value === 'storageOrder') {
  1069. return item.recptCode || item.recpt_code || ''
  1070. }
  1071. return ''
  1072. }
  1073. function getItemCode(item: any): string {
  1074. if (selectorType.value === 'supplier') {
  1075. return item.vendorCode || item.code || ''
  1076. } else if (selectorType.value === 'contract') {
  1077. return item.contract_number || item.contractNumber || ''
  1078. }
  1079. return ''
  1080. }
  1081. // 获取供应商名称(采购订单/入库单)
  1082. function getItemVendor(item: any): string {
  1083. if (selectorType.value === 'purchaseOrder' || selectorType.value === 'storageOrder') {
  1084. return item.vendorName || item.vendor_name || ''
  1085. }
  1086. return ''
  1087. }
  1088. // 获取总金额(采购订单/入库单)
  1089. function getItemAmount(item: any): string {
  1090. if (selectorType.value === 'purchaseOrder' || selectorType.value === 'storageOrder') {
  1091. const amount = item.totalAmount || item.total_amount
  1092. if (amount) {
  1093. return Number(amount).toFixed(2)
  1094. }
  1095. }
  1096. return ''
  1097. }
  1098. // 获取单据状态(采购订单/入库单)
  1099. function getItemStatus(item: any): string {
  1100. if (selectorType.value === 'purchaseOrder' || selectorType.value === 'storageOrder') {
  1101. const status = item.status
  1102. if (status === 'CONFIRMED') {
  1103. return '已确认'
  1104. } else if (status === 'FINISHED') {
  1105. return '已完成'
  1106. }
  1107. }
  1108. return ''
  1109. }
  1110. // 获取状态样式类
  1111. function getStatusClass(item: any): string {
  1112. const status = item.status
  1113. if (status === 'CONFIRMED') {
  1114. return 'status-confirmed'
  1115. } else if (status === 'FINISHED') {
  1116. return 'status-finished'
  1117. }
  1118. return ''
  1119. }
  1120. // 获取创建时间(采购订单/入库单)
  1121. function getItemCreateTime(item: any): string {
  1122. if (selectorType.value === 'purchaseOrder' || selectorType.value === 'storageOrder') {
  1123. return item.createTime || item.create_time || ''
  1124. }
  1125. return ''
  1126. }
  1127. // 获取合同类型名称(根据 contract_type 值)
  1128. function getContractTypeName(item: any): string {
  1129. const type = item.contract_type
  1130. if (!type) return ''
  1131. const typeMap: Record<string, string> = {
  1132. '1': '销售合同',
  1133. '2': '采购合同',
  1134. '3': '技术服务合同'
  1135. }
  1136. // 优先使用后端返回的 contract_type_name,如果没有则根据 contract_type 转换
  1137. return item.contract_type_name || typeMap[String(type)] || ''
  1138. }
  1139. // 检查入库单是否已选中
  1140. function isStorageOrderSelected(item: any): boolean {
  1141. if (selectorType.value !== 'storageOrder') return false
  1142. return selectedStorageOrders.value.some(order =>
  1143. order.recptCode === (item.recptCode || item.recpt_code)
  1144. )
  1145. }
  1146. // 切换入库单选择状态
  1147. function toggleStorageOrderSelection(item: any) {
  1148. if (selectorType.value !== 'storageOrder') {
  1149. // 非入库单类型,直接选择并关闭
  1150. selectItem(item)
  1151. return
  1152. }
  1153. const recptCode = item.recptCode || item.recpt_code
  1154. const index = selectedStorageOrders.value.findIndex(order => order.recptCode === recptCode)
  1155. if (index > -1) {
  1156. // 已选中,取消选择
  1157. selectedStorageOrders.value.splice(index, 1)
  1158. } else {
  1159. // 未选中,添加到选择列表
  1160. selectedStorageOrders.value.push({
  1161. recptCode: recptCode,
  1162. recptId: item.recptId || item.recpt_id,
  1163. recptName: item.recptName || item.recpt_name,
  1164. vendorName: item.vendorName || item.vendor_name,
  1165. totalAmount: item.totalAmount || item.total_amount
  1166. })
  1167. }
  1168. }
  1169. // 确认入库单选择
  1170. function confirmStorageOrderSelection() {
  1171. if (selectedStorageOrders.value.length === 0) {
  1172. $modal.msg('请至少选择一个入库单!')
  1173. return
  1174. }
  1175. // 将选中的入库单编号拼接(用逗号分隔)
  1176. const storageOrderNos = selectedStorageOrders.value.map(order => order.recptCode).join(',')
  1177. const storageOrderIds = selectedStorageOrders.value.map(order => order.recptId).join(',')
  1178. baseForm.value.storageOrderNo = storageOrderNos
  1179. baseForm.value.orderIdFromMes = storageOrderIds
  1180. // 清空合同、采购订单
  1181. baseForm.value.contractId = ''
  1182. baseForm.value.contractNumber = ''
  1183. baseForm.value.contractName = ''
  1184. baseForm.value.purchaseOrderNo = ''
  1185. closePopup()
  1186. }
  1187. function selectItem(item: any) {
  1188. // 入库单使用多选逻辑,不走这里
  1189. if (selectorType.value === 'storageOrder') return
  1190. if (selectorType.value === 'supplier') {
  1191. baseForm.value.payeeName = item.vendorName || item.name || ''
  1192. baseForm.value.vendorCode = item.vendorCode || item.code || ''
  1193. // 清空合同、采购订单、入库单
  1194. clearRelatedOrders()
  1195. } else if (selectorType.value === 'contract') {
  1196. // 检查合同类型,只允许采购合同(2)和技术服务合同(3)
  1197. const contractType = item.contract_type
  1198. if (contractType !== 2 && contractType !== 3) {
  1199. $modal.msg('只能选择采购合同或技术服务合同!')
  1200. return
  1201. }
  1202. baseForm.value.contractId = item.universalid || item.id || ''
  1203. baseForm.value.contractNumber = item.contract_number || item.contractNumber || ''
  1204. baseForm.value.contractName = item.contract_name || item.contractName || ''
  1205. // 回填供应商信息
  1206. baseForm.value.payeeName = item.supplierName || item.supplier_name || ''
  1207. baseForm.value.vendorCode = item.supplierCode || item.supplier_code || ''
  1208. // 清空采购订单、入库单
  1209. baseForm.value.purchaseOrderNo = ''
  1210. baseForm.value.storageOrderNo = ''
  1211. baseForm.value.orderIdFromMes = ''
  1212. } else if (selectorType.value === 'purchaseOrder') {
  1213. baseForm.value.purchaseOrderNo = item.purchaseCode || item.purchase_code || ''
  1214. baseForm.value.orderIdFromMes = item.purchaseId || item.purchase_id || ''
  1215. // 清空合同、入库单
  1216. baseForm.value.contractId = ''
  1217. baseForm.value.contractNumber = ''
  1218. baseForm.value.contractName = ''
  1219. baseForm.value.storageOrderNo = ''
  1220. }
  1221. closePopup()
  1222. }
  1223. // 清除合同
  1224. function clearContract() {
  1225. baseForm.value.contractId = ''
  1226. baseForm.value.contractNumber = ''
  1227. baseForm.value.contractName = ''
  1228. // 注意:不清空收款单位和供应商编码
  1229. }
  1230. // 清除采购订单
  1231. function clearPurchaseOrder() {
  1232. baseForm.value.purchaseOrderNo = ''
  1233. baseForm.value.orderIdFromMes = ''
  1234. }
  1235. // 清除入库单
  1236. function clearStorageOrder() {
  1237. baseForm.value.storageOrderNo = ''
  1238. baseForm.value.orderIdFromMes = ''
  1239. // 清空多选列表
  1240. selectedStorageOrders.value = []
  1241. }
  1242. // 清空所有关联信息
  1243. function clearRelatedOrders() {
  1244. baseForm.value.contractId = ''
  1245. baseForm.value.contractNumber = ''
  1246. baseForm.value.contractName = ''
  1247. baseForm.value.purchaseOrderNo = ''
  1248. baseForm.value.storageOrderNo = ''
  1249. baseForm.value.orderIdFromMes = ''
  1250. }
  1251. // 处理审批意见签名
  1252. const currentApprovalFieldName = ref('')
  1253. const approvalSealInfo = ref<Record<string, any>>({})
  1254. function handleApprovalSignature(fieldName: string) {
  1255. currentApprovalFieldName.value = fieldName
  1256. signaturePopupShow.value = false
  1257. nextTick(() => {
  1258. signaturePopupShow.value = true
  1259. })
  1260. signaturePopup.value.open()
  1261. }
  1262. function handleAutoSeal(fieldName: string) {
  1263. const elem = getApprovalElement(fieldName)
  1264. if (elem) {
  1265. getSeal(userStore.user.useId).then(({ returnParams }) => {
  1266. const elem = getApprovalElement(fieldName)
  1267. elem.defaultValue = returnParams.sealFileId.universalid
  1268. elem.sealImgPath = returnParams.sealFileId.path
  1269. approvalSealInfo.value[fieldName] = {
  1270. sealInsId: returnParams.sealFileId.universalid,
  1271. sealFileId: returnParams.sealFileId.universalid,
  1272. left: 0,
  1273. top: 0
  1274. }
  1275. }).catch(err => {
  1276. $modal.msgError('获取签名失败:' + err)
  1277. })
  1278. }
  1279. }
  1280. function initSignature() {
  1281. signaturePopupShow.value = false
  1282. setTimeout(() => {
  1283. signaturePopupShow.value = true
  1284. }, 100)
  1285. }
  1286. function onclickSignatureButton(event: string) {
  1287. switch (event) {
  1288. case 'undo':
  1289. signatureRef.value.undo()
  1290. break
  1291. case 'clear':
  1292. signatureRef.value.clear()
  1293. break
  1294. case 'save':
  1295. signatureRef.value.canvasToTempFilePath({
  1296. success: (res: any) => {
  1297. if (res.isEmpty) {
  1298. $modal.msgError('签名不能为空!')
  1299. return
  1300. }
  1301. if (res.tempFilePath.substring(0, 'data:image/png;base64,'.length) == 'data:image/png;base64,') {
  1302. const _fileData = res.tempFilePath
  1303. uploadSignatureBoardImg(userStore.user.useId, _fileData, getApprovalElement(currentApprovalFieldName.value).tableField)
  1304. .then(({ returnParams }) => {
  1305. const elem = getApprovalElement(currentApprovalFieldName.value)
  1306. elem.defaultValue = returnParams.sealInsID
  1307. elem.sealImgPath = returnParams.path
  1308. // 保存印章信息(用于后端处理)
  1309. // sealInsID 是手写签名的实例 ID,但 imgval 需要使用印章模板 ID
  1310. // 对于手写签名,印章模板 ID 与签名实例 ID 相同
  1311. approvalSealInfo.value[currentApprovalFieldName.value] = {
  1312. sealInsId: returnParams.sealInsID, // 签名实例 ID
  1313. sealFileId: returnParams.sealInsID, // 印章模板 ID(手写签名时与实例 ID 相同)
  1314. left: 0, // 默认左边距为 0
  1315. top: 0 // 默认上边距为 0
  1316. }
  1317. currentApprovalFieldName.value = ''
  1318. signaturePopupShow.value = false
  1319. signaturePopup.value.close()
  1320. })
  1321. } else {
  1322. uni.getFileSystemManager().readFile({
  1323. filePath: res.tempFilePath,
  1324. encoding: 'base64',
  1325. success: (fileData: any) => {
  1326. const _fileData = 'data:image/png;base64,' + fileData.data
  1327. uploadSignatureBoardImg(userStore.user.useId, _fileData, getApprovalElement(currentApprovalFieldName.value).tableField)
  1328. .then(({ returnParams }) => {
  1329. const elem = getApprovalElement(currentApprovalFieldName.value)
  1330. elem.defaultValue = returnParams.sealInsID
  1331. elem.sealImgPath = returnParams.path
  1332. // 保存印章信息(用于后端处理)
  1333. // sealInsID 是手写签名的实例 ID,但 imgval 需要使用印章模板 ID
  1334. // 对于手写签名,印章模板 ID 与签名实例 ID 相同
  1335. approvalSealInfo.value[currentApprovalFieldName.value] = {
  1336. sealInsId: returnParams.sealInsID, // 签名实例 ID
  1337. sealFileId: returnParams.sealInsID, // 印章模板 ID(手写签名时与实例 ID 相同)
  1338. left: 0, // 默认左边距为 0
  1339. top: 0 // 默认上边距为 0
  1340. }
  1341. currentApprovalFieldName.value = ''
  1342. signaturePopupShow.value = false
  1343. signaturePopup.value.close()
  1344. })
  1345. }
  1346. })
  1347. }
  1348. }
  1349. })
  1350. break
  1351. case 'landscape':
  1352. isLandscape.value = !isLandscape.value
  1353. initSignature()
  1354. break
  1355. }
  1356. }
  1357. function clearSignature() {
  1358. onclickSignatureButton('clear')
  1359. }
  1360. function saveSignature() {
  1361. onclickSignatureButton('save')
  1362. }
  1363. function closeSignature() {
  1364. signaturePopupShow.value = false
  1365. signaturePopup.value.close()
  1366. }
  1367. // 暴露验证方法给父组件
  1368. defineExpose({
  1369. validate: async () => {
  1370. if (!baseForm.value.payerName) {
  1371. return Promise.reject(new Error('请输入单位'))
  1372. }
  1373. if (!baseForm.value.payeeName) {
  1374. return Promise.reject(new Error('请选择收款单位'))
  1375. }
  1376. if (!baseForm.value.amountLower || Number(baseForm.value.amountLower) <= 0) {
  1377. return Promise.reject(new Error('请输入有效的付款金额'))
  1378. }
  1379. if (!baseForm.value.paymentType) {
  1380. return Promise.reject(new Error('请选择付款类型'))
  1381. }
  1382. if (!baseForm.value.paymentMethod) {
  1383. return Promise.reject(new Error('请输入付款方式'))
  1384. }
  1385. if (!baseForm.value.paymentDate) {
  1386. return Promise.reject(new Error('请选择付款日期'))
  1387. }
  1388. if (!baseForm.value.purpose) {
  1389. return Promise.reject(new Error('请输入用途或理由'))
  1390. }
  1391. if (!baseForm.value.payeeBankAccount) {
  1392. return Promise.reject(new Error('请输入开户行及账号'))
  1393. }
  1394. if (!baseForm.value.operator) {
  1395. return Promise.reject(new Error('请选择经办人'))
  1396. }
  1397. if (!baseForm.value.accountId) {
  1398. return Promise.reject(new Error('没有默认付款账户!请检查是否已设置默认账户。'))
  1399. }
  1400. // 验证审批意见字段
  1401. const approvalFields = [
  1402. { fieldName: 'dept_approver', msg: '部门负责人签名' },
  1403. { fieldName: 'accountant', msg: '经办会计签名' },
  1404. { fieldName: 'finance_manager', msg: '财务经理签名' },
  1405. { fieldName: 'deputy_manager_dept', msg: '分管副总部门签名' },
  1406. { fieldName: 'deputy_manager_audit', msg: '分管副总审计签名' },
  1407. { fieldName: 'general_manager', msg: '公司总经理签名' },
  1408. { fieldName: 'chairman', msg: '董事长签名' }
  1409. ]
  1410. for (const field of approvalFields) {
  1411. const elem = getApprovalElement(field.fieldName)
  1412. if (elem) {
  1413. if (props.editableFields && props.editableFields.includes(field.fieldName)) {
  1414. if (!elem.defaultValue || elem.defaultValue === '') {
  1415. return Promise.reject(new Error(field.msg + '不能为空!'))
  1416. }
  1417. }
  1418. }
  1419. }
  1420. return Promise.resolve()
  1421. },
  1422. getFormElements: () => {
  1423. const formElements: any[] = []
  1424. Object.keys(baseForm.value).forEach(key => {
  1425. const value = baseForm.value[key]
  1426. if (value !== undefined && value !== null) {
  1427. formElements.push({
  1428. name: key,
  1429. value: String(value),
  1430. type: '0'
  1431. })
  1432. }
  1433. })
  1434. const approvalFields = ['dept_approver', 'accountant', 'finance_manager', 'deputy_manager_dept', 'deputy_manager_audit', 'general_manager', 'chairman']
  1435. approvalFields.forEach(fieldName => {
  1436. const elem = getApprovalElement(fieldName)
  1437. if (elem) {
  1438. formElements.push({
  1439. name: fieldName,
  1440. value: elem.defaultValue || '',
  1441. type: elem.type || '0'
  1442. })
  1443. // 添加签名图片的 imgval 值(用于后端处理)
  1444. // 格式:sealFileId_left_top(必须使用印章模板 ID,而不是签名实例 ID)
  1445. if (approvalSealInfo.value[fieldName]) {
  1446. const sealInfo = approvalSealInfo.value[fieldName]
  1447. formElements.push({
  1448. name: fieldName + '_imgval',
  1449. value: `${sealInfo.sealFileId}_${sealInfo.left}_${sealInfo.top}`,
  1450. type: '0'
  1451. })
  1452. } else if (elem.sealImgPath && elem.defaultValue) {
  1453. // 兼容旧数据:如果有 sealImgPath 和 defaultValue,提取 sealId 构建 imgval
  1454. // sealImgPath 格式:/shares/document/seal/593268258724500.png
  1455. const sealIdMatch = elem.sealImgPath.match(/\/seal\/(\d+)\.png$/)
  1456. if (sealIdMatch) {
  1457. const sealId = sealIdMatch[1]
  1458. formElements.push({
  1459. name: fieldName + '_imgval',
  1460. value: `${sealId}_0_0`,
  1461. type: '0'
  1462. })
  1463. }
  1464. }
  1465. }
  1466. })
  1467. return formElements
  1468. },
  1469. getFormData: () => {
  1470. const formData: any = {
  1471. ...baseForm.value
  1472. }
  1473. const approvalFields = ['dept_approver', 'accountant', 'finance_manager', 'deputy_manager_dept', 'deputy_manager_audit', 'general_manager', 'chairman']
  1474. approvalFields.forEach(fieldName => {
  1475. const elem = getApprovalElement(fieldName)
  1476. if (elem) {
  1477. formData[fieldName] = elem.defaultValue || ''
  1478. // 添加签名图片的 imgval 值(用于后端处理)
  1479. // 格式:sealFileId_left_top(必须使用印章模板 ID,而不是签名实例 ID)
  1480. if (approvalSealInfo.value[fieldName]) {
  1481. const sealInfo = approvalSealInfo.value[fieldName]
  1482. formData[fieldName + '_imgval'] = `${sealInfo.sealFileId}_${sealInfo.left}_${sealInfo.top}`
  1483. } else if (elem.sealImgPath && elem.defaultValue) {
  1484. // 兼容旧数据:如果有 sealImgPath 和 defaultValue,提取 sealId 构建 imgval
  1485. // sealImgPath 格式:/shares/document/seal/593268258724500.png
  1486. const sealIdMatch = elem.sealImgPath.match(/\/seal\/(\d+)\.png$/)
  1487. if (sealIdMatch) {
  1488. const sealId = sealIdMatch[1]
  1489. formData[fieldName + '_imgval'] = `${sealId}_0_0`
  1490. }
  1491. }
  1492. }
  1493. })
  1494. return formData
  1495. }
  1496. })
  1497. </script>
  1498. <style lang="scss" scoped>
  1499. .payment-form-component {
  1500. ::v-deep .uni-forms {
  1501. .uni-forms-item__content {
  1502. .uni-easyinput__content-input {
  1503. font-size: calc(14px + 1.2*(1rem - 16px)) !important;
  1504. font-weight: 500;
  1505. color: #333;
  1506. }
  1507. }
  1508. }
  1509. .selector-wrapper {
  1510. padding: 8px 10px;
  1511. border: 1px solid #e5e5e5;
  1512. border-radius: 4px;
  1513. min-height: 36px;
  1514. display: flex;
  1515. align-items: center;
  1516. cursor: pointer;
  1517. &:active {
  1518. background-color: #f5f5f5;
  1519. }
  1520. .placeholder {
  1521. color: #999;
  1522. }
  1523. }
  1524. // 收款单位输入框和按钮的布局样式
  1525. .payee-input-wrapper {
  1526. display: flex;
  1527. flex-direction: column;
  1528. gap: 8px;
  1529. ::v-deep .uni-easyinput {
  1530. width: 100%;
  1531. }
  1532. .select-supplier-btn {
  1533. width: 100%;
  1534. }
  1535. }
  1536. .picker-wrapper {
  1537. padding: 8px 10px;
  1538. border: 1px solid #e5e5e5;
  1539. border-radius: 4px;
  1540. min-height: 36px;
  1541. display: flex;
  1542. align-items: center;
  1543. .placeholder {
  1544. color: #999;
  1545. }
  1546. }
  1547. .textarea-input {
  1548. width: 100%;
  1549. min-height: 80px;
  1550. padding: 8px 10px;
  1551. border: 1px solid #e5e5e5;
  1552. border-radius: 4px;
  1553. font-size: 14px;
  1554. }
  1555. .textarea-display {
  1556. width: 100%;
  1557. min-height: 36px;
  1558. padding: 8px 10px;
  1559. border: 1px solid #e5e5e5;
  1560. border-radius: 4px;
  1561. font-size: 14px;
  1562. color: #333;
  1563. line-height: 1.5;
  1564. }
  1565. .element_value_container {
  1566. .signature_img {
  1567. width: 180px;
  1568. }
  1569. }
  1570. .signature_container {
  1571. background-color: #f5f5f5;
  1572. height: 40vh;
  1573. width: 90vw;
  1574. .signature_content {
  1575. height: 80%;
  1576. width: 100%;
  1577. }
  1578. .signature_button_container {
  1579. height: 20%;
  1580. width: 100%;
  1581. button {
  1582. height: 100%;
  1583. }
  1584. }
  1585. }
  1586. .signature_container_landscape {
  1587. height: 100vh;
  1588. width: 100vw;
  1589. .signature_content {
  1590. height: 85%;
  1591. width: 100%;
  1592. }
  1593. .signature_button_container {
  1594. margin-top: 10%;
  1595. height: 15%;
  1596. width: 100%;
  1597. button {
  1598. height: 100%;
  1599. width: 100%;
  1600. transform: rotate(90deg);
  1601. }
  1602. }
  1603. }
  1604. .selector-popup {
  1605. width: 90%;
  1606. max-width: 500px;
  1607. height: 60vh; // 固定高度
  1608. background-color: #fff;
  1609. border-radius: 12px;
  1610. overflow: hidden;
  1611. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
  1612. position: relative;
  1613. margin: 0 auto;
  1614. display: flex;
  1615. flex-direction: column; // 垂直布局
  1616. .popup-header {
  1617. display: flex;
  1618. justify-content: space-between;
  1619. align-items: center;
  1620. padding: 15px;
  1621. border-bottom: 1px solid #e5e5e5;
  1622. .popup-title {
  1623. font-size: 16px;
  1624. font-weight: bold;
  1625. }
  1626. }
  1627. .popup-content-wrapper {
  1628. flex: 1;
  1629. display: flex;
  1630. flex-direction: column;
  1631. overflow: hidden;
  1632. padding-bottom: 50px; // 为固定底部按钮留出空间
  1633. .search-bar {
  1634. display: flex;
  1635. gap: 10px;
  1636. padding: 10px 15px;
  1637. align-items: center;
  1638. }
  1639. .filter-bar {
  1640. padding: 10px 15px;
  1641. background-color: #fff;
  1642. border-bottom: 1px solid #e5e5e5;
  1643. .picker-filter {
  1644. padding: 8px 12px;
  1645. background-color: #f5f5f5;
  1646. border-radius: 4px;
  1647. font-size: 14px;
  1648. color: #333;
  1649. }
  1650. }
  1651. .selected-tip {
  1652. padding: 10px 15px;
  1653. background-color: #f0f9eb;
  1654. border-bottom: 1px solid #e5e5e5;
  1655. color: #67c23a;
  1656. font-size: 13px;
  1657. }
  1658. .popup-content {
  1659. flex: 1;
  1660. overflow-y: auto;
  1661. .selector-item {
  1662. padding: 10px 12px;
  1663. border-bottom: 1px solid #f0f0f0;
  1664. &:active {
  1665. background-color: #f5f5f5;
  1666. }
  1667. &.selected {
  1668. background-color: #f0f9eb;
  1669. }
  1670. .selector-item-content {
  1671. display: flex;
  1672. justify-content: space-between;
  1673. align-items: center;
  1674. gap: 8px;
  1675. // 入库单复选框样式
  1676. .checkbox-wrapper {
  1677. width: 24px;
  1678. height: 24px;
  1679. display: flex;
  1680. align-items: center;
  1681. justify-content: center;
  1682. flex-shrink: 0;
  1683. .checkbox-icon {
  1684. font-size: 18px;
  1685. color: #4caf50;
  1686. font-weight: bold;
  1687. }
  1688. }
  1689. .item-info {
  1690. flex: 1;
  1691. display: flex;
  1692. flex-wrap: wrap;
  1693. align-items: center;
  1694. gap: 4px;
  1695. .item-name {
  1696. font-size: 15px;
  1697. font-weight: bold;
  1698. color: #333;
  1699. flex-basis: 100%;
  1700. margin-bottom: 4px;
  1701. }
  1702. .item-code,
  1703. .item-spec {
  1704. font-size: 13px;
  1705. color: #666;
  1706. white-space: nowrap;
  1707. font-weight: 500;
  1708. }
  1709. .item-vendor {
  1710. font-size: 13px;
  1711. color: #999;
  1712. white-space: nowrap;
  1713. }
  1714. .item-amount {
  1715. font-size: 13px;
  1716. color: #f76560;
  1717. white-space: nowrap;
  1718. font-weight: 500;
  1719. }
  1720. .item-status {
  1721. font-size: 12px;
  1722. padding: 2px 8px;
  1723. border-radius: 4px;
  1724. white-space: nowrap;
  1725. &.status-confirmed {
  1726. background-color: #ffecdb;
  1727. color: #ff8800;
  1728. }
  1729. &.status-finished {
  1730. background-color: #e8f5e9;
  1731. color: #4caf50;
  1732. }
  1733. }
  1734. .item-time {
  1735. font-size: 12px;
  1736. color: #999;
  1737. white-space: nowrap;
  1738. flex-basis: 100%;
  1739. margin-top: 2px;
  1740. }
  1741. .item-type {
  1742. font-size: 12px;
  1743. color: #ff6b6b;
  1744. white-space: nowrap;
  1745. margin-left: 4px;
  1746. }
  1747. .item-code::after {
  1748. content: ' | ';
  1749. margin: 0 4px;
  1750. color: #ddd;
  1751. }
  1752. }
  1753. }
  1754. }
  1755. .loading-text {
  1756. text-align: center;
  1757. padding: 12px;
  1758. color: #909399;
  1759. font-size: 13px;
  1760. }
  1761. .no-more-text {
  1762. text-align: center;
  1763. padding: 12px;
  1764. color: #909399;
  1765. font-size: 13px;
  1766. }
  1767. .empty-data {
  1768. text-align: center;
  1769. padding: 30px;
  1770. color: #909399;
  1771. }
  1772. // 入库单多选提示
  1773. .selected-tip {
  1774. padding: 8px 12px;
  1775. background-color: #e8f5e9;
  1776. color: #4caf50;
  1777. font-size: 13px;
  1778. text-align: center;
  1779. border-bottom: 1px solid #c8e6c9;
  1780. }
  1781. }
  1782. }
  1783. }
  1784. // 入库单确认按钮区域(固定在底部)
  1785. .popup-footer-fixed {
  1786. position: absolute;
  1787. bottom: 0;
  1788. left: 0;
  1789. right: 0;
  1790. padding: 12px;
  1791. border-top: 1px solid #e5e5e5;
  1792. background-color: #fff;
  1793. display: flex;
  1794. gap: 12px;
  1795. z-index: 10;
  1796. box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
  1797. }
  1798. .popup-footer-fixed .cancel-btn {
  1799. flex: 1 !important;
  1800. height: 44px !important;
  1801. line-height: 44px !important;
  1802. font-size: 16px !important;
  1803. background-color: #f5f5f5 !important;
  1804. color: #333 !important;
  1805. border: none !important;
  1806. border-radius: 8px !important;
  1807. margin: 0 !important;
  1808. padding: 0 !important;
  1809. }
  1810. .popup-footer-fixed .confirm-btn {
  1811. flex: 1 !important;
  1812. height: 44px !important;
  1813. line-height: 44px !important;
  1814. font-size: 16px !important;
  1815. background-color: #007aff !important;
  1816. color: #fff !important;
  1817. border: none !important;
  1818. border-radius: 8px !important;
  1819. margin: 0 !important;
  1820. padding: 0 !important;
  1821. }
  1822. }
  1823. // 基本信息中的禁用字段样式优化(参考 edit/index.vue)
  1824. ::v-deep .uni-forms {
  1825. .uni-forms-item__content {
  1826. .uni-easyinput__content-input {
  1827. font-size: calc(14px + 1.2*(1rem - 16px)) !important;
  1828. font-weight: 500;
  1829. color: #333;
  1830. }
  1831. .uni-date {
  1832. .uni-icons {
  1833. font-size: calc(22px + 1.2*(1rem - 16px)) !important;
  1834. font-weight: 500;
  1835. }
  1836. .uni-date__x-input {
  1837. height: auto;
  1838. font-size: calc(14px + 1.2*(1rem - 16px)) !important;
  1839. font-weight: 500;
  1840. color: #333;
  1841. }
  1842. }
  1843. }
  1844. }
  1845. </style>