edit.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <div class="app-container">
  3. <!-- 商品报价信息 -->
  4. <el-card shadow="never" class="form-card">
  5. <div class="section-title">
  6. <el-button type="primary" @click="handleAddProduct">添加商品报价</el-button>
  7. <el-button plain @click="handleDownloadTemplate">下载模板</el-button>
  8. <el-button plain @click="handleImportClick">导入</el-button>
  9. <input
  10. ref="fileInputRef"
  11. type="file"
  12. accept=".xlsx,.xls"
  13. style="display: none"
  14. @change="handleFileChange"
  15. />
  16. </div>
  17. <el-table :data="productList" border style="width: 100%; margin-top: 20px;">
  18. <el-table-column prop="productNo" label="产品编号" align="center" width="120" />
  19. <el-table-column label="产品图片" align="center" width="100">
  20. <template #default="scope">
  21. <el-image
  22. v-if="scope.row.productImage"
  23. :src="scope.row.productImage"
  24. style="width: 60px; height: 60px;"
  25. fit="cover"
  26. />
  27. </template>
  28. </el-table-column>
  29. <el-table-column prop="itemName" label="产品名称" align="center" show-overflow-tooltip />
  30. <el-table-column label="产品分类" align="center" width="180" show-overflow-tooltip>
  31. <template #default="scope">
  32. <span>{{ scope.row.topCategoryName }}-{{ scope.row.mediumCategoryName }}-{{ scope.row.bottomCategoryName }}</span>
  33. </template>
  34. </el-table-column>
  35. <el-table-column prop="brandName" label="品牌" align="center" width="100" />
  36. <el-table-column prop="unitName" label="单位" align="center" width="80" />
  37. <el-table-column label="市场价" align="center" width="100">
  38. <template #default="scope">
  39. <span>¥{{ scope.row.marketPrice || '0.00' }}</span>
  40. </template>
  41. </el-table-column>
  42. <el-table-column label="官网价" align="center" width="100">
  43. <template #default="scope">
  44. <span>¥{{ scope.row.memberPrice || '0.00' }}</span>
  45. </template>
  46. </el-table-column>
  47. <el-table-column label="供应价(元)" align="center" width="120">
  48. <template #default="scope">
  49. <span>¥{{ scope.row.supplyPrice || '0.00' }}</span>
  50. </template>
  51. </el-table-column>
  52. <el-table-column label="操作" align="center" width="100" fixed="right">
  53. <template #default="scope">
  54. <el-button link type="primary" @click="handleDeleteProduct(scope.row, scope.$index)">删除</el-button>
  55. </template>
  56. </el-table-column>
  57. </el-table>
  58. <pagination
  59. v-show="total > 0"
  60. :total="total"
  61. v-model:page="queryParams.pageNum"
  62. v-model:limit="queryParams.pageSize"
  63. @pagination="getProductData"
  64. />
  65. </el-card>
  66. <!-- 添加商品对话框 -->
  67. <Product
  68. v-model:visible="productDialog.visible"
  69. v-model:modelValue="productList"
  70. @confirm="handleProductConfirm"
  71. />
  72. </div>
  73. </template>
  74. <script setup lang="ts">
  75. import { ref, onMounted, reactive, getCurrentInstance } from 'vue';
  76. import { useRouter, useRoute } from 'vue-router';
  77. import { getContractProductList, delContracproductBySupplier, importTemplate, importData } from '@/api/product/contracproduct';
  78. import { ElMessage } from 'element-plus';
  79. import Product from './components/Product.vue';
  80. const { proxy } = getCurrentInstance() as any;
  81. const router = useRouter();
  82. const route = useRoute();
  83. const productList = ref<any[]>([]);
  84. const total = ref(0);
  85. const fileInputRef = ref<HTMLInputElement>();
  86. const queryParams = reactive({
  87. pageNum: 1,
  88. pageSize: 10
  89. });
  90. // 商品对话框相关
  91. const productDialog = reactive({
  92. visible: false
  93. });
  94. /** 返回上一页 */
  95. const goBack = () => {
  96. router.back();
  97. };
  98. /** 获取商品列表 */
  99. const getProductData = async () => {
  100. try {
  101. const res = await getContractProductList({
  102. supplierId: route.params.id,
  103. pageNum: queryParams.pageNum,
  104. pageSize: queryParams.pageSize
  105. } as any);
  106. productList.value = res.rows || []
  107. total.value = (res as any).total || 0;
  108. } catch (e) {
  109. console.error('获取商品列表失败:', e);
  110. ElMessage.error('获取商品列表失败');
  111. }
  112. };
  113. /** 添加商品 */
  114. const handleAddProduct = () => {
  115. productDialog.visible = true;
  116. };
  117. /** 删除商品 */
  118. const handleDeleteProduct = async (row: any, index: number) => {
  119. console.log('handleDeleteProduct', row, index);
  120. try {
  121. await proxy?.$modal.confirm('是否确认删除该商品报价数据项?');
  122. await delContracproductBySupplier(row.id, route.params.id as string);
  123. proxy?.$modal.msgSuccess('删除成功');
  124. await getProductData();
  125. } catch (e) {
  126. // 用户取消或请求失败时不提示
  127. }
  128. };
  129. /** 商品确认添加后,刷新列表 */
  130. const handleProductConfirm = () => {
  131. getProductData();
  132. };
  133. /** 下载导入模板 */
  134. const handleDownloadTemplate = async () => {
  135. try {
  136. const res = await importTemplate();
  137. const blob = new Blob([res as any], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
  138. const url = window.URL.createObjectURL(blob);
  139. const link = document.createElement('a');
  140. link.href = url;
  141. link.download = '合同产品导入模板.xlsx';
  142. link.click();
  143. window.URL.revokeObjectURL(url);
  144. ElMessage.success('模板下载成功');
  145. } catch (e) {
  146. console.error('下载模板失败:', e);
  147. ElMessage.error('下载模板失败');
  148. }
  149. };
  150. /** 触发文件选择 */
  151. const handleImportClick = () => {
  152. fileInputRef.value?.click();
  153. };
  154. /** 导入文件 */
  155. const handleFileChange = async (event: Event) => {
  156. const input = event.target as HTMLInputElement;
  157. const file = input.files?.[0];
  158. if (!file) return;
  159. try {
  160. await proxy?.$modal.confirm('是否确认导入该文件中的商品数据?');
  161. await importData(file);
  162. proxy?.$modal.msgSuccess('导入成功');
  163. await getProductData();
  164. } catch (e) {
  165. // 用户取消或请求失败时不提示
  166. if (e !== 'cancel') {
  167. console.error('导入失败:', e);
  168. }
  169. } finally {
  170. // 重置文件选择,以便可以重复选择同一文件
  171. input.value = '';
  172. }
  173. };
  174. onMounted(() => {
  175. getProductData();
  176. });
  177. </script>
  178. <style scoped>
  179. .app-container {
  180. background: #f0f2f5;
  181. min-height: 100vh;
  182. padding: 0;
  183. }
  184. .detail-header {
  185. background: #fff;
  186. padding: 16px 24px;
  187. display: flex;
  188. align-items: center;
  189. border-bottom: 1px solid #e8e8e8;
  190. margin-bottom: 16px;
  191. }
  192. .back-icon {
  193. font-size: 18px;
  194. cursor: pointer;
  195. margin-right: 12px;
  196. color: #666;
  197. }
  198. .back-icon:hover {
  199. color: #409eff;
  200. }
  201. .header-title {
  202. font-weight: 500;
  203. color: #333;
  204. }
  205. .form-card {
  206. margin: 16px;
  207. padding: 24px;
  208. }
  209. .section-title {
  210. margin: 20px 0;
  211. padding-bottom: 12px;
  212. border-bottom: 1px solid #e8e8e8;
  213. }
  214. </style>