index.vue 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. <template>
  2. <div class="p-2">
  3. <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
  4. <div v-show="showSearch" class="mb-[10px]">
  5. <el-card shadow="hover">
  6. <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="120">
  7. <el-form-item label="报备单号" prop="preparedNo">
  8. <el-input v-model="queryParams.preparedNo" placeholder="请输入" clearable @keyup.enter="handleQuery" />
  9. </el-form-item>
  10. <el-form-item label="合作项目/平台" prop="cooperationCode">
  11. <el-select v-model="queryParams.cooperationCode" placeholder="请选择" clearable>
  12. <el-option v-for="dict in cooperation_code" :key="dict.value" :label="dict.label" :value="dict.value" />
  13. </el-select>
  14. </el-form-item>
  15. <el-form-item label="伙伴商名称" prop="customerName">
  16. <el-input v-model="queryParams.customerName" placeholder="请输入" clearable @keyup.enter="handleQuery" />
  17. </el-form-item>
  18. <el-form-item label="报备状态" prop="preparedStatus">
  19. <el-select v-model="queryParams.preparedStatus" placeholder="请选择" clearable>
  20. <el-option v-for="dict in prepared_status" :key="dict.value" :label="dict.label" :value="dict.value" />
  21. </el-select>
  22. </el-form-item>
  23. <el-form-item label="成交状态" prop="dealStatus">
  24. <el-select v-model="queryParams.dealStatus" placeholder="请选择" clearable>
  25. <el-option v-for="dict in deal_status" :key="dict.value" :label="dict.label" :value="dict.value" />
  26. </el-select>
  27. </el-form-item>
  28. <el-form-item>
  29. <el-button type="primary" @click="handleQuery">搜索</el-button>
  30. <el-button @click="resetQuery">重置</el-button>
  31. </el-form-item>
  32. </el-form>
  33. </el-card>
  34. </div>
  35. </transition>
  36. <el-card shadow="hover">
  37. <template #header>
  38. <el-row :gutter="10">
  39. <!-- <el-col :span="1.5">
  40. <el-button type="primary" plain icon="Plus" @click="handleAdd">新增报备</el-button>
  41. </el-col>
  42. <el-col :span="1.5">
  43. <el-button type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">修改</el-button>
  44. </el-col>
  45. <el-col :span="1.5">
  46. <el-button type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">删除</el-button>
  47. </el-col> -->
  48. <el-col :span="1.5">
  49. <el-button type="warning" plain icon="Download" @click="handleExport">导出</el-button>
  50. </el-col>
  51. <!-- <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar> -->
  52. </el-row>
  53. </template>
  54. <el-table
  55. v-loading="loading"
  56. border
  57. :data="orderList"
  58. @selection-change="handleSelectionChange"
  59. row-key="id"
  60. :expand-row-keys="expandedRows"
  61. @expand-change="handleExpandChange"
  62. >
  63. <el-table-column type="expand">
  64. <template #default="scope">
  65. <div style="padding: 10px 50px; background: #f5f7fa">
  66. <el-table :data="scope.row.products || []" border size="small">
  67. <el-table-column label="产品编号" prop="productNo" width="120" />
  68. <el-table-column label="产品名称" prop="productName" min-width="150" />
  69. <el-table-column label="数量" prop="quantity" width="80" align="center" />
  70. <el-table-column label="单价" prop="price" width="100" align="center" />
  71. </el-table>
  72. <el-empty v-if="!scope.row.products?.length" description="暂无商品" :image-size="60" />
  73. </div>
  74. </template>
  75. </el-table-column>
  76. <el-table-column label="报备单号" align="center" prop="preparedNo" min-width="160" />
  77. <el-table-column label="合作项目/平台" align="center" prop="cooperationCode" min-width="120">
  78. <template #default="scope">
  79. <el-tag type="success" size="small">{{ getDictLabel(cooperation_code, scope.row.cooperationCode) }}</el-tag>
  80. </template>
  81. </el-table-column>
  82. <el-table-column label="金额" align="center" prop="amount" min-width="80" />
  83. <el-table-column label="创建时间" align="center" prop="createTime" min-width="160" />
  84. <el-table-column label="报备到期日" align="center" prop="dueDate" min-width="120">
  85. <template #default="scope">
  86. <span>{{ parseTime(scope.row.dueDate, '{y}-{m}-{d}') }}</span>
  87. </template>
  88. </el-table-column>
  89. <el-table-column label="报备状态" align="center" prop="preparedStatus" min-width="80">
  90. <template #default="scope">
  91. <el-checkbox :model-value="scope.row.preparedStatus === 1" disabled />
  92. </template>
  93. </el-table-column>
  94. <el-table-column label="成交状态" align="center" prop="dealStatus" min-width="80">
  95. <template #default="scope">
  96. <el-checkbox :model-value="scope.row.dealStatus === 2" disabled />
  97. </template>
  98. </el-table-column>
  99. <el-table-column label="操作" align="center" min-width="200">
  100. <template #default="scope">
  101. <div style="display: flex; flex-wrap: wrap; gap: 4px; justify-content: center">
  102. <!-- <el-button link type="primary" @click="handleUpdate(scope.row)">编辑</el-button> -->
  103. <!-- <el-button link type="success" @click="handleSubmit(scope.row)">提交</el-button> -->
  104. <el-button link type="danger" @click="handleReject(scope.row)">驳回</el-button>
  105. <el-button link type="primary" @click="handleConfirm(scope.row)">确认</el-button>
  106. <el-button link type="info" @click="handleViewAttachment(scope.row)">查看附件</el-button>
  107. </div>
  108. </template>
  109. </el-table-column>
  110. </el-table>
  111. <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
  112. </el-card>
  113. <el-drawer v-model="dialog.visible" :title="dialog.title" direction="rtl" size="75%" @close="cancel" :close-on-click-modal="true">
  114. <el-form ref="orderFormRef" :model="form" :rules="rules" label-width="120px">
  115. <!-- 基本信息 -->
  116. <el-divider content-position="left">
  117. <span style="font-size: 16px; font-weight: 500">📋 基本信息</span>
  118. </el-divider>
  119. <el-row :gutter="20">
  120. <el-col :span="8">
  121. <el-form-item label="合作项目/伙伴" prop="cooperationCode">
  122. <el-select v-model="form.cooperationCode" placeholder="请选择合作项目/伙伴" style="width: 100%">
  123. <el-option v-for="dict in cooperation_code" :key="dict.value" :label="dict.label" :value="dict.value" />
  124. </el-select>
  125. </el-form-item>
  126. </el-col>
  127. <el-col :span="8">
  128. <el-form-item label="客户名称" prop="customerName">
  129. <el-input v-model="form.customerName" placeholder="请输入客户名称" />
  130. </el-form-item>
  131. </el-col>
  132. <el-col :span="8">
  133. <el-form-item label="活动截止日期" prop="dueDate">
  134. <el-date-picker v-model="form.dueDate" type="date" placeholder="请选择活动截止日期" value-format="YYYY-MM-DD" style="width: 100%" />
  135. </el-form-item>
  136. </el-col>
  137. </el-row>
  138. <el-row :gutter="20">
  139. <el-col :span="8">
  140. <el-form-item label="金额" prop="amount">
  141. <el-input v-model="form.amount" placeholder="请输入金额" />
  142. </el-form-item>
  143. </el-col>
  144. <el-col :span="8">
  145. <el-form-item label="设计下单时间" prop="estimatedTime">
  146. <el-date-picker
  147. v-model="form.estimatedTime"
  148. type="date"
  149. placeholder="请选择设计下单时间"
  150. value-format="YYYY-MM-DD"
  151. style="width: 100%"
  152. />
  153. </el-form-item>
  154. </el-col>
  155. <el-col :span="8">
  156. <el-form-item label="报备到期日" prop="dueDate">
  157. <el-date-picker v-model="form.dueDate" type="date" placeholder="请选择报备到期日" value-format="YYYY-MM-DD" style="width: 100%" />
  158. </el-form-item>
  159. </el-col>
  160. </el-row>
  161. <el-row :gutter="20">
  162. <el-col :span="24">
  163. <el-form-item label="联系地址" prop="area">
  164. <el-input v-model="form.area" placeholder="请输入联系地址" />
  165. </el-form-item>
  166. </el-col>
  167. </el-row>
  168. <el-row :gutter="20">
  169. <el-col :span="24">
  170. <el-form-item label="发票附件" prop="invoiceAttachment">
  171. <el-upload
  172. class="upload-demo"
  173. :action="uploadAction"
  174. :headers="uploadHeaders"
  175. :on-success="handleUploadSuccess"
  176. :on-error="handleUploadError"
  177. :file-list="fileList"
  178. :limit="5"
  179. :on-exceed="handleExceed"
  180. >
  181. <el-button size="small" type="primary">合同上传</el-button>
  182. </el-upload>
  183. <div style="color: #999; font-size: 12px; margin-top: 4px">支持jpg/png/pdf/xlsx等文件,最多5个</div>
  184. </el-form-item>
  185. </el-col>
  186. </el-row>
  187. <!-- 状态信息(仅编辑时显示) -->
  188. <template v-if="form.id">
  189. <el-divider content-position="left">
  190. <span style="font-size: 16px; font-weight: 500">📊 状态信息</span>
  191. </el-divider>
  192. <el-row :gutter="20">
  193. <el-col :span="12">
  194. <el-form-item label="报备状态" prop="preparedStatus">
  195. <el-select v-model="form.preparedStatus" disabled style="width: 100%">
  196. <el-option v-for="dict in prepared_status" :key="dict.value" :label="dict.label" :value="dict.value" />
  197. </el-select>
  198. </el-form-item>
  199. </el-col>
  200. <el-col :span="12">
  201. <el-form-item label="成交状态" prop="dealStatus">
  202. <el-select v-model="form.dealStatus" disabled style="width: 100%">
  203. <el-option v-for="dict in deal_status" :key="dict.value" :label="dict.label" :value="dict.value" />
  204. </el-select>
  205. </el-form-item>
  206. </el-col>
  207. </el-row>
  208. </template>
  209. <!-- 客户信息 -->
  210. <el-divider content-position="left">
  211. <span style="font-size: 16px; font-weight: 500">👥 客户信息</span>
  212. </el-divider>
  213. <el-row :gutter="20">
  214. <el-col :span="8">
  215. <el-form-item label="采购单位" prop="purchasingUnit">
  216. <el-input v-model="form.purchasingUnit" placeholder="请输入采购单位" />
  217. </el-form-item>
  218. </el-col>
  219. <el-col :span="8">
  220. <el-form-item label="下单人" prop="person">
  221. <el-input v-model="form.person" placeholder="请输入下单人" />
  222. </el-form-item>
  223. </el-col>
  224. <el-col :span="8">
  225. <el-form-item label="下单日期" prop="estimatedTime">
  226. <el-date-picker v-model="form.estimatedTime" type="date" placeholder="请选择下单日期" value-format="YYYY-MM-DD" style="width: 100%" />
  227. </el-form-item>
  228. </el-col>
  229. </el-row>
  230. <el-row :gutter="20">
  231. <el-col :span="8">
  232. <el-form-item label="下单手机号" prop="phone">
  233. <el-input v-model="form.phone" placeholder="请输入下单手机号" />
  234. </el-form-item>
  235. </el-col>
  236. </el-row>
  237. <!-- 报备商品信息 -->
  238. <el-divider content-position="left">
  239. <span style="font-size: 16px; font-weight: 500">📦 报备商品信息</span>
  240. </el-divider>
  241. <div style="margin-bottom: 10px">
  242. <el-button type="primary" icon="Plus" @click="handleAddProduct">新增商品</el-button>
  243. <el-button type="success" icon="Upload" @click="handleImportProduct">导入商品</el-button>
  244. </div>
  245. <el-table :data="productList" border style="width: 100%">
  246. <el-table-column label="产品编号" align="center" prop="productNo" width="120" />
  247. <el-table-column label="平台编号" align="center" prop="platformNo" width="120" />
  248. <el-table-column label="产品名称" align="center" prop="productName" width="150" />
  249. <el-table-column label="产品图片" align="center" prop="productImage" width="100">
  250. <template #default="scope">
  251. <el-image v-if="scope.row.productImage" :src="scope.row.productImage" style="width: 50px; height: 50px" fit="cover" />
  252. <span v-else>-</span>
  253. </template>
  254. </el-table-column>
  255. <el-table-column label="品牌" align="center" prop="brand" width="100" />
  256. <el-table-column label="品类" align="center" prop="category" width="100" />
  257. <el-table-column label="单价" align="center" prop="price" width="100" />
  258. <el-table-column label="数量" align="center" prop="quantity" width="100" />
  259. <el-table-column label="金额" align="center" prop="amount" width="100" />
  260. </el-table>
  261. <el-empty v-if="!productList.length" description="暂无数据" :image-size="100" style="margin: 20px 0" />
  262. </el-form>
  263. <template #footer>
  264. <div style="text-align: right">
  265. <el-button @click="cancel">取消</el-button>
  266. <el-button type="primary" @click="submitForm">提交</el-button>
  267. </div>
  268. </template>
  269. </el-drawer>
  270. <el-dialog v-model="auditDialog.visible" title="订单报备审核" width="600px" append-to-body @close="cancelAudit">
  271. <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="100px">
  272. <el-form-item label="审核结果" prop="status">
  273. <el-radio-group v-model="auditForm.status">
  274. <el-radio :label="1">通过</el-radio>
  275. <el-radio :label="2">驳回</el-radio>
  276. </el-radio-group>
  277. </el-form-item>
  278. <el-form-item label="审核意见" prop="auditRemark">
  279. <el-input v-model="auditForm.auditRemark" type="textarea" :rows="4" placeholder="请输入审核意见" />
  280. </el-form-item>
  281. </el-form>
  282. <template #footer>
  283. <div class="dialog-footer">
  284. <el-button @click="cancelAudit">取 消</el-button>
  285. <el-button type="primary" @click="submitAudit">确 定</el-button>
  286. </div>
  287. </template>
  288. </el-dialog>
  289. <!-- 商品选择抽屉 -->
  290. <el-drawer v-model="productDrawer.visible" title="商品信息" direction="rtl" size="60%" :close-on-click-modal="true">
  291. <div style="margin-bottom: 20px; display: flex; gap: 10px">
  292. <el-select v-model="productQuery.cooperationCode" placeholder="请选择" clearable style="width: 200px">
  293. <el-option v-for="dict in cooperation_code" :key="dict.value" :label="dict.label" :value="dict.value" />
  294. </el-select>
  295. <el-input v-model="productQuery.productNo" placeholder="请输入" clearable style="width: 200px" />
  296. <el-button type="primary" @click="searchProducts">搜索</el-button>
  297. <el-button @click="resetProductQuery">重置</el-button>
  298. <el-button style="margin-left: auto">展开</el-button>
  299. </div>
  300. <el-table :data="availableProducts" border @selection-change="handleProductSelectionChange" max-height="750">
  301. <el-table-column type="selection" width="55" align="center" />
  302. <el-table-column label="合作项目/平台" align="center" prop="cooperationCode" width="120" />
  303. <el-table-column label="商品编号" align="center" prop="productNo" width="120">
  304. <template #default="scope">
  305. <span style="color: #409eff">{{ scope.row.productNo }}</span>
  306. </template>
  307. </el-table-column>
  308. <el-table-column label="平台编号" align="center" prop="platformNo" width="100" />
  309. <el-table-column label="商品图片" align="center" prop="productImage" width="80">
  310. <template #default="scope">
  311. <el-image v-if="scope.row.productImage" :src="scope.row.productImage" style="width: 50px; height: 50px" fit="cover" />
  312. <span v-else>--</span>
  313. </template>
  314. </el-table-column>
  315. <el-table-column label="商品信息" align="center" prop="productName" min-width="180" show-overflow-tooltip />
  316. <el-table-column label="商品类别" align="center" prop="category" width="100" />
  317. <el-table-column label="单位" align="center" prop="unit" width="60" />
  318. <el-table-column label="SKU价格" align="center" width="120">
  319. <template #default="scope">
  320. <div style="text-align: left; line-height: 1.8">
  321. <div style="color: #909399; font-size: 12px">市场价:¥{{ scope.row.marketPrice }}</div>
  322. <div style="color: #f56c6c; font-size: 12px">会员价:¥{{ scope.row.memberPrice }}</div>
  323. <div style="color: #67c23a; font-size: 12px">底价:¥{{ scope.row.basePrice }}</div>
  324. </div>
  325. </template>
  326. </el-table-column>
  327. <el-table-column label="库存数量" align="center" width="100">
  328. <template #default="scope">
  329. <div style="text-align: left; line-height: 1.8">
  330. <div style="color: #f56c6c; font-size: 12px">库存总数:{{ scope.row.stockTotal || 0 }}</div>
  331. <div style="color: #67c23a; font-size: 12px">可售库存:{{ scope.row.stockAvailable || 0 }}</div>
  332. <div style="color: #909399; font-size: 12px">成本库存:{{ scope.row.stockCost || 0 }}</div>
  333. </div>
  334. </template>
  335. </el-table-column>
  336. <el-table-column label="上下架状态" align="center" width="100">
  337. <template #default="scope">
  338. <el-tag :type="scope.row.shelfStatus === 1 ? 'success' : 'info'" size="small">
  339. {{ scope.row.shelfStatus === 1 ? '上架' : '下架' }}
  340. </el-tag>
  341. </template>
  342. </el-table-column>
  343. </el-table>
  344. <el-pagination
  345. v-model:current-page="productQuery.pageNum"
  346. v-model:page-size="productQuery.pageSize"
  347. :total="productTotal"
  348. :page-sizes="[10, 20, 50, 100]"
  349. layout="total, sizes, prev, pager, next, jumper"
  350. @size-change="searchProducts"
  351. @current-change="searchProducts"
  352. style="margin-top: 20px; justify-content: center"
  353. />
  354. <template #footer>
  355. <div class="dialog-footer">
  356. <el-button @click="productDrawer.visible = false">取 消</el-button>
  357. <el-button type="primary" @click="confirmAddProducts">确 定</el-button>
  358. </div>
  359. </template>
  360. </el-drawer>
  361. <!-- 附件查看抽屉 -->
  362. <el-drawer v-model="attachmentDrawer.visible" title="查看附件" direction="rtl" size="50%" :close-on-click-modal="true">
  363. <el-table :data="attachmentDrawer.attachments" border style="width: 100%">
  364. <el-table-column label="附件名称" prop="name" />
  365. <el-table-column label="附件预览" align="center">
  366. <template #default="scope">
  367. <el-image
  368. v-if="isImageFile(scope.row.url)"
  369. :src="scope.row.url"
  370. style="width: 100px; height: 100px"
  371. fit="cover"
  372. :preview-src-list="attachmentDrawer.attachments.map((a: any) => a.url)"
  373. />
  374. <span v-else>{{ getFileTypeLabel(scope.row.url) }}</span>
  375. </template>
  376. </el-table-column>
  377. <el-table-column label="操作" align="center" width="150">
  378. <template #default="scope">
  379. <el-button link type="primary" @click="downloadFile(scope.row.url)">下载</el-button>
  380. </template>
  381. </el-table-column>
  382. </el-table>
  383. <el-empty v-if="!attachmentDrawer.attachments.length" description="暂无附件" :image-size="100" style="margin: 20px 0" />
  384. </el-drawer>
  385. </div>
  386. </template>
  387. <script setup name="OrderReport" lang="ts">
  388. import { getCurrentInstance, reactive, ref, toRefs, computed } from 'vue';
  389. import {
  390. listPartnerPrepared,
  391. getPartnerPrepared,
  392. addPartnerPrepared,
  393. updatePartnerPrepared,
  394. delPartnerPrepared,
  395. exportPartnerPrepared,
  396. listPartnerPreparedProduct,
  397. addPartnerPreparedProduct,
  398. delPartnerPreparedProduct,
  399. listSiteProducts
  400. } from '@/api/partner/prepared';
  401. import { PartnerPreparedVO, PartnerPreparedForm, PartnerPreparedQuery } from '@/api/partner/prepared/types';
  402. import { ComponentInternalInstance } from 'vue';
  403. import { getToken } from '@/utils/auth';
  404. import { listByIds } from '@/api/system/oss';
  405. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  406. // 获取字典数据
  407. const { cooperation_code, prepared_status, deal_status } = toRefs<any>(proxy?.useDict('cooperation_code', 'prepared_status', 'deal_status'));
  408. const orderList = ref([]);
  409. const productList = ref([]);
  410. const availableProducts = ref([]);
  411. const selectedProducts = ref([]);
  412. const loading = ref(true);
  413. const productTotal = ref(0);
  414. const showSearch = ref(true);
  415. const ids = ref([]);
  416. const single = ref(true);
  417. const multiple = ref(true);
  418. const total = ref(0);
  419. const expandedRows = ref<string[]>([]);
  420. const fileList = ref<any[]>([]);
  421. // 文件上传配置
  422. const uploadAction = computed(() => import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload?clientid=' + import.meta.env.VITE_APP_CLIENT_ID);
  423. const uploadHeaders = computed(() => ({
  424. Authorization: 'Bearer ' + getToken()
  425. }));
  426. /** 获取字典标签 */
  427. function getDictLabel(dictList: any[], value: any) {
  428. if (!dictList || !value) return '';
  429. const dict = dictList.find((d: any) => d.value === value);
  430. return dict?.label || value;
  431. }
  432. /** 展开行变化 */
  433. async function handleExpandChange(row: any, expandedRowsList: any[]) {
  434. expandedRows.value = expandedRowsList.map((r: any) => r.id);
  435. // 如果是展开,加载商品数据
  436. if (expandedRowsList.includes(row) && !row.products) {
  437. const productRes: any = await listPartnerPreparedProduct({ partnerPreparedId: row.id, pageNum: 1, pageSize: 100 });
  438. const products = productRes.rows || [];
  439. // 根据 productId 查询商品详情
  440. if (products.length > 0) {
  441. const siteProductRes: any = await listSiteProducts({ pageNum: 1, pageSize: 1000 });
  442. const siteProducts = siteProductRes.data?.rows || [];
  443. row.products = products.map((item: any) => {
  444. const siteProduct = siteProducts.find((p: any) => p.id == item.productId);
  445. return {
  446. ...item,
  447. productNo: siteProduct?.productNo || '',
  448. productName: siteProduct?.productName || ''
  449. };
  450. });
  451. } else {
  452. row.products = [];
  453. }
  454. }
  455. }
  456. /** 提交按钮 */
  457. function handleSubmit(row: any) {
  458. proxy?.$modal
  459. .confirm('是否确认提交该订单报备?')
  460. .then(() => {
  461. const updateData: PartnerPreparedForm = {
  462. id: row.id,
  463. preparedStatus: 0 // 提交后变为待审核
  464. };
  465. return updatePartnerPrepared(updateData);
  466. })
  467. .then(() => {
  468. proxy?.$modal.msgSuccess('提交成功');
  469. getList();
  470. })
  471. .catch(() => {});
  472. }
  473. /** 确认按钮 */
  474. function handleConfirm(row: any) {
  475. proxy?.$modal
  476. .confirm('是否确认该订单报备?')
  477. .then(() => {
  478. const updateData: PartnerPreparedForm = {
  479. id: row.id,
  480. preparedStatus: 1, // 已通过
  481. dealStatus: 2 // 已完成
  482. };
  483. return updatePartnerPrepared(updateData);
  484. })
  485. .then(() => {
  486. proxy?.$modal.msgSuccess('确认成功');
  487. getList();
  488. })
  489. .catch(() => {});
  490. }
  491. const data = reactive({
  492. form: {} as PartnerPreparedForm,
  493. queryParams: {
  494. pageNum: 1,
  495. pageSize: 10,
  496. preparedNo: undefined,
  497. cooperationCode: undefined,
  498. customerName: undefined,
  499. preparedStatus: undefined,
  500. dealStatus: undefined
  501. } as PartnerPreparedQuery,
  502. rules: {
  503. customerName: [{ required: true, message: '客户名称不能为空', trigger: 'blur' }]
  504. },
  505. dialog: {
  506. visible: false,
  507. title: ''
  508. },
  509. auditDialog: {
  510. visible: false
  511. },
  512. productDrawer: {
  513. visible: false
  514. },
  515. attachmentDrawer: {
  516. visible: false,
  517. attachments: []
  518. },
  519. productQuery: {
  520. pageNum: 1,
  521. pageSize: 10,
  522. cooperationCode: undefined,
  523. productNo: undefined
  524. },
  525. auditForm: {
  526. id: undefined,
  527. status: 1,
  528. auditRemark: ''
  529. },
  530. auditRules: {
  531. status: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
  532. auditRemark: [{ required: true, message: '审核意见不能为空', trigger: 'blur' }]
  533. }
  534. });
  535. const { queryParams, form, rules, dialog, auditDialog, productDrawer, attachmentDrawer, productQuery, auditForm, auditRules } = toRefs(data);
  536. const queryFormRef = ref();
  537. const orderFormRef = ref();
  538. const auditFormRef = ref();
  539. function getList() {
  540. loading.value = true;
  541. listPartnerPrepared(queryParams.value)
  542. .then((res: any) => {
  543. orderList.value = res.rows;
  544. total.value = res.total;
  545. loading.value = false;
  546. })
  547. .catch(() => {
  548. loading.value = false;
  549. });
  550. }
  551. function handleQuery() {
  552. queryParams.value.pageNum = 1;
  553. getList();
  554. }
  555. function resetQuery() {
  556. queryFormRef.value?.resetFields();
  557. handleQuery();
  558. }
  559. function reset() {
  560. form.value = {
  561. id: undefined,
  562. partnerId: undefined,
  563. preparedNo: undefined,
  564. cooperationCode: undefined,
  565. customerName: undefined,
  566. purchasingUnitName: undefined,
  567. amount: undefined,
  568. area: undefined,
  569. estimatedTime: undefined,
  570. dueDate: undefined,
  571. invoiceAttachment: undefined,
  572. purchasingUnit: undefined,
  573. person: undefined,
  574. deptName: undefined,
  575. phone: undefined,
  576. preparedStatus: undefined,
  577. dealStatus: undefined,
  578. reject: undefined,
  579. status: '0',
  580. remark: undefined
  581. };
  582. productList.value = [];
  583. orderFormRef.value?.resetFields();
  584. }
  585. function handleAdd() {
  586. reset();
  587. dialog.value.visible = true;
  588. dialog.value.title = '新增订单报备';
  589. }
  590. function handleUpdate(row?: any) {
  591. reset();
  592. const id = row?.id || ids.value[0];
  593. getPartnerPrepared(id).then((res: any) => {
  594. // 使用 Object.assign 更新 form.value,而不是直接替换
  595. Object.assign(form.value, res.data);
  596. // 加载已有附件到文件列表
  597. if (res.data.invoiceAttachment) {
  598. const urls = res.data.invoiceAttachment.split(',');
  599. fileList.value = urls.map((url: string, index: number) => ({
  600. name: `附件${index + 1}`,
  601. url: url
  602. }));
  603. } else {
  604. fileList.value = [];
  605. }
  606. dialog.value.visible = true;
  607. dialog.value.title = '修改订单报备';
  608. // 加载产品明细
  609. listPartnerPreparedProduct({ partnerPreparedId: id, pageNum: 1, pageSize: 100 }).then(async (productRes: any) => {
  610. const products = productRes.rows || [];
  611. // 根据 productId 查询商品详情补充显示信息
  612. if (products.length > 0) {
  613. // 获取所有商品信息
  614. const siteProductRes: any = await listSiteProducts({ pageNum: 1, pageSize: 1000 });
  615. const siteProducts = siteProductRes.data?.rows || [];
  616. // 合并商品信息
  617. productList.value = products.map((item: any) => {
  618. const siteProduct = siteProducts.find((p: any) => p.id == item.productId);
  619. return {
  620. ...item,
  621. productNo: siteProduct?.productNo || '',
  622. productName: siteProduct?.productName || '',
  623. productImage: siteProduct?.productImage || '',
  624. category: siteProduct?.isSelf === 1 ? '自营商品' : '非自营商品',
  625. platformNo: ''
  626. };
  627. });
  628. } else {
  629. productList.value = [];
  630. }
  631. });
  632. });
  633. }
  634. function submitForm() {
  635. orderFormRef.value?.validate((valid: boolean) => {
  636. if (valid) {
  637. if (form.value.id) {
  638. // 修改订单
  639. updatePartnerPrepared(form.value)
  640. .then(() => {
  641. // 保存商品列表
  642. return saveProductList(form.value.id as number);
  643. })
  644. .then(() => {
  645. proxy?.$modal.msgSuccess('修改成功');
  646. dialog.value.visible = false;
  647. getList();
  648. });
  649. } else {
  650. // 新增订单
  651. addPartnerPrepared(form.value)
  652. .then((res: any) => {
  653. const orderId = res.data;
  654. // 保存商品列表
  655. return saveProductList(orderId);
  656. })
  657. .then(() => {
  658. proxy?.$modal.msgSuccess('新增成功');
  659. dialog.value.visible = false;
  660. getList();
  661. });
  662. }
  663. }
  664. });
  665. }
  666. /** 保存商品列表 */
  667. async function saveProductList(partnerPreparedId: number) {
  668. if (!productList.value.length) return;
  669. // 逐个保存商品(如果是新增的商品)
  670. for (const product of productList.value) {
  671. if (!product.id) {
  672. // 新增商品 - 只传必要字段
  673. await addPartnerPreparedProduct({
  674. partnerPreparedId: partnerPreparedId,
  675. productId: product.productId,
  676. quantity: product.quantity || 1,
  677. price: product.price || 0
  678. });
  679. }
  680. }
  681. }
  682. function cancel() {
  683. dialog.value.visible = false;
  684. reset();
  685. }
  686. function handleDelete(row?: any) {
  687. const orderIds = row?.id || ids.value;
  688. proxy?.$modal
  689. .confirm('是否确认删除订单报备编号为"' + orderIds + '"的数据项?')
  690. .then(() => {
  691. return delPartnerPrepared(orderIds);
  692. })
  693. .then(() => {
  694. proxy?.$modal.msgSuccess('删除成功');
  695. getList();
  696. })
  697. .catch(() => {});
  698. }
  699. function handleExport() {
  700. exportPartnerPrepared(queryParams.value).then((response: any) => {
  701. proxy?.$download.zip(response.data, `prepared_${new Date().getTime()}.xlsx`);
  702. });
  703. }
  704. function handleSelectionChange(selection: any[]) {
  705. ids.value = selection.map((item) => item.id);
  706. single.value = selection.length !== 1;
  707. multiple.value = !selection.length;
  708. }
  709. function handleAudit(row: any) {
  710. auditForm.value.id = row.id;
  711. auditForm.value.status = 1;
  712. auditForm.value.auditRemark = '';
  713. auditDialog.value.visible = true;
  714. }
  715. function submitAudit() {
  716. auditFormRef.value?.validate((valid: boolean) => {
  717. if (valid) {
  718. const updateData: PartnerPreparedForm = {
  719. id: auditForm.value.id,
  720. preparedStatus: auditForm.value.status,
  721. reject: auditForm.value.status === 2 ? auditForm.value.auditRemark : undefined
  722. };
  723. updatePartnerPrepared(updateData).then(() => {
  724. proxy?.$modal.msgSuccess('审核成功');
  725. auditDialog.value.visible = false;
  726. getList();
  727. });
  728. }
  729. });
  730. }
  731. function cancelAudit() {
  732. auditDialog.value.visible = false;
  733. auditForm.value = {
  734. id: undefined,
  735. status: 1,
  736. auditRemark: ''
  737. };
  738. }
  739. function handleVerify(row: any) {
  740. proxy?.$modal
  741. .confirm('是否确认核实该订单报备?')
  742. .then(() => {
  743. const updateData: PartnerPreparedForm = {
  744. id: row.id,
  745. preparedStatus: 1
  746. };
  747. return updatePartnerPrepared(updateData);
  748. })
  749. .then(() => {
  750. proxy?.$modal.msgSuccess('核实成功');
  751. getList();
  752. });
  753. }
  754. function handleReject(row: any) {
  755. proxy?.$modal
  756. .confirm('是否确认驳回该订单报备?')
  757. .then(() => {
  758. const updateData: PartnerPreparedForm = {
  759. id: row.id,
  760. preparedStatus: 2,
  761. reject: '驳回'
  762. };
  763. return updatePartnerPrepared(updateData);
  764. })
  765. .then(() => {
  766. proxy?.$modal.msgSuccess('驳回成功');
  767. getList();
  768. })
  769. .catch(() => {});
  770. }
  771. const isImageFile = (url: string) => {
  772. return url && /\.(jpg|jpeg|png|gif|webp)$/i.test(url);
  773. };
  774. const getFileTypeLabel = (url: string) => {
  775. if (!url) return '未知文件';
  776. const ext = url.split('.').pop()?.toLowerCase();
  777. return ext ? `${ext.toUpperCase()} 文件` : '未知文件';
  778. };
  779. const downloadFile = (url: string) => {
  780. if (url) {
  781. window.open(url, '_blank');
  782. }
  783. };
  784. async function handleViewAttachment(row: any) {
  785. if (row.invoiceAttachment) {
  786. try {
  787. const res = await listByIds(row.invoiceAttachment);
  788. const ossList = res.data || [];
  789. attachmentDrawer.value.attachments = ossList.map((oss: any) => ({
  790. name: oss.originalName || '附件',
  791. url: oss.url
  792. }));
  793. attachmentDrawer.value.visible = true;
  794. } catch (error) {
  795. console.error('获取附件信息失败:', error);
  796. proxy?.$modal.msgError('获取附件信息失败');
  797. }
  798. } else {
  799. proxy?.$modal.msgWarning('该订单暂无附件');
  800. }
  801. }
  802. /** 文件上传成功 */
  803. function handleUploadSuccess(response: any, file: any, fileListParam: any[]) {
  804. console.log('上传成功回调 - response:', response);
  805. console.log('上传成功回调 - file:', file);
  806. console.log('上传成功回调 - fileListParam:', fileListParam);
  807. if (response.code === 200) {
  808. // 更新 fileList
  809. fileList.value = fileListParam;
  810. // 收集所有已上传成功的文件URL
  811. const urls = fileListParam
  812. .filter((f: any) => f.response?.code === 200 || f.url)
  813. .map((f: any) => f.response?.data?.url || f.url)
  814. .filter(Boolean);
  815. console.log('提取的URLs:', urls);
  816. form.value.invoiceAttachment = urls.join(',');
  817. console.log('保存到表单的附件:', form.value.invoiceAttachment);
  818. proxy?.$modal.msgSuccess('文件上传成功');
  819. } else {
  820. proxy?.$modal.msgError(response.msg || '文件上传失败');
  821. }
  822. }
  823. /** 文件上传失败 */
  824. function handleUploadError() {
  825. proxy?.$modal.msgError('文件上传失败,请重试');
  826. }
  827. /** 文件数量超出限制 */
  828. function handleExceed() {
  829. proxy?.$modal.msgWarning('最多只能上传5个文件');
  830. }
  831. function handleAddProduct() {
  832. productDrawer.value.visible = true;
  833. searchProducts();
  834. }
  835. function handleImportProduct() {
  836. proxy?.$modal.msg('导入商品功能开发中...');
  837. }
  838. function searchProducts() {
  839. listSiteProducts(productQuery.value)
  840. .then((res: any) => {
  841. availableProducts.value = (res.data?.rows || []).map((item: any) => ({
  842. id: item.id,
  843. cooperationCode: '--',
  844. productNo: item.productNo,
  845. platformNo: '--',
  846. productImage: item.productImage,
  847. productName: item.productName,
  848. category: item.isSelf === 1 ? '自营商品' : '非自营商品',
  849. unit: item.unitName || '包',
  850. marketPrice: item.marketPrice,
  851. memberPrice: item.memberPrice,
  852. basePrice: item.minSellingPrice,
  853. stockTotal: item.totalInventory || 0,
  854. stockAvailable: item.nowInventory || 0,
  855. stockCost: item.virtualInventory || 0,
  856. shelfStatus: item.productStatus
  857. }));
  858. productTotal.value = res.data?.total || 0;
  859. })
  860. .catch(() => {
  861. availableProducts.value = [];
  862. productTotal.value = 0;
  863. });
  864. }
  865. function resetProductQuery() {
  866. productQuery.value = {
  867. pageNum: 1,
  868. pageSize: 10,
  869. cooperationCode: undefined,
  870. productNo: undefined
  871. };
  872. searchProducts();
  873. }
  874. function handleProductSelectionChange(selection: any[]) {
  875. selectedProducts.value = selection;
  876. }
  877. function confirmAddProducts() {
  878. if (selectedProducts.value.length === 0) {
  879. proxy?.$modal.msgWarning('请至少选择一个商品');
  880. return;
  881. }
  882. let addedCount = 0;
  883. // 将选中的商品添加到产品列表
  884. selectedProducts.value.forEach((product: any) => {
  885. const exists = productList.value.find((p: any) => p.productId === product.id);
  886. if (!exists) {
  887. const price = product.memberPrice || 0;
  888. productList.value.push({
  889. productId: product.id,
  890. productNo: product.productNo,
  891. platformNo: product.platformNo || '',
  892. productName: product.productName,
  893. productImage: product.productImage,
  894. brand: '',
  895. category: product.category,
  896. price: price,
  897. quantity: 1,
  898. amount: price
  899. });
  900. addedCount++;
  901. }
  902. });
  903. if (addedCount > 0) {
  904. proxy?.$modal.msgSuccess(`成功添加 ${addedCount} 个商品`);
  905. } else {
  906. proxy?.$modal.msgWarning('所选商品已存在于列表中');
  907. }
  908. productDrawer.value.visible = false;
  909. selectedProducts.value = [];
  910. }
  911. getList();
  912. </script>
  913. <style scoped lang="scss"></style>