Sen descrición

patch_waybill_attachment.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. # -*- coding: utf-8 -*-
  2. """Patch waybill index.vue and WaybillDetail.vue for attachment and list columns."""
  3. import os
  4. import re
  5. ROOT = os.path.dirname(os.path.dirname(__file__))
  6. INDEX = os.path.join(ROOT, "ruoyi-ui", "src", "views", "basic", "waybill", "index.vue")
  7. DETAIL = os.path.join(ROOT, "ruoyi-ui", "src", "views", "basic", "waybill", "components", "WaybillDetail.vue")
  8. TABLE_OLD = r''' <el-table border stripe v-loading="loading" :data="waybillList" @selection-change="handleSelectionChange">
  9. <el-table-column type="selection" width="55" align="center" />
  10. <el-table-column label="\u8fd0\u5355\u53f7" prop="waybillNo" min-width="150" align="center" show-overflow-tooltip />
  11. <el-table-column label="\u8fd0\u8f93\u7c7b\u578b" prop="transportType" width="110" align="center" />
  12. <el-table-column label="\u5355\u636e\u65e5\u671f" prop="documentDate" width="110" align="center" />
  13. <el-table-column label="\u53d1\u8d27\u4f01\u4e1a" prop="senderCompany" min-width="140" align="center" show-overflow-tooltip />
  14. <el-table-column label="\u6536\u8d27\u4f01\u4e1a" prop="receiverCompany" min-width="140" align="center" show-overflow-tooltip />
  15. <el-table-column label="\u9884\u8ba1\u53d1\u8f66" prop="planDepartTime" width="160" align="center" />
  16. <el-table-column label="\u9884\u8ba1\u5230\u8fbe" prop="planArriveTime" width="160" align="center" />
  17. <el-table-column label="\u603b\u6570\u91cf" prop="totalQuantity" width="90" align="right" />
  18. <el-table-column label="\u603b\u91cd\u91cf(kg)" prop="totalWeight" width="100" align="right" />
  19. <el-table-column label="\u5ba1\u6838\u72b6\u6001" prop="auditStatus" width="100" align="center">
  20. <template slot-scope="scope">
  21. <el-tag :type="auditTagType(scope.row.auditStatus)" size="small">{{ auditStatusLabel(scope.row.auditStatus) }}</el-tag>
  22. </template>
  23. </el-table-column>'''
  24. TABLE_NEW = r''' <el-table border stripe v-loading="loading" :data="waybillList" @selection-change="handleSelectionChange">
  25. <el-table-column type="selection" width="55" align="center" />
  26. <el-table-column label="\u8fd0\u5355\u7f16\u53f7" prop="waybillNo" min-width="140" align="center" show-overflow-tooltip />
  27. <el-table-column label="\u53d1\u8fd0\u65e5\u671f" prop="documentDate" width="110" align="center" />
  28. <el-table-column label="\u6536\u8d27\u5ba2\u6237" prop="receiverCompany" min-width="130" align="center" show-overflow-tooltip />
  29. <el-table-column label="\u8d77\u6b62\u5730\u70b9" min-width="160" align="center" show-overflow-tooltip>
  30. <template slot-scope="scope">{{ formatRoute(scope.row) }}</template>
  31. </el-table-column>
  32. <el-table-column label="\u8f66\u724c" prop="carNum" width="100" align="center" />
  33. <el-table-column label="\u53f8\u673a" prop="driverName" width="90" align="center" />
  34. <el-table-column label="\u5f53\u524d\u6e29\u5ea6" width="100" align="center">
  35. <template slot-scope="scope">
  36. <span :class="{ 'text-danger': isTempOverLimit(scope.row) }">{{ displayCurrentTemp(scope.row) }}</span>
  37. </template>
  38. </el-table-column>
  39. <el-table-column label="\u72b6\u6001" width="90" align="center">
  40. <template slot-scope="scope">
  41. <el-tag :type="transportStatusTag(scope.row)" size="small">{{ transportStatusText(scope.row) }}</el-tag>
  42. </template>
  43. </el-table-column>
  44. <el-table-column label="\u5230\u8fbe\u65f6\u95f4" prop="planArriveTime" width="160" align="center" />'''
  45. FORM_INSERT = r'''
  46. <el-divider content-position="left">\u8fd0\u5355\u9644\u4ef6</el-divider>
  47. <el-row :gutter="12">
  48. <el-col :span="24">
  49. <el-form-item label="\u8fd0\u5355\u6587\u4ef6">
  50. <file-upload v-model="form.attachment" :limit="10" :file-size="20" />
  51. </el-form-item>
  52. </el-col>
  53. </el-row>
  54. '''
  55. METHODS_INSERT = r'''
  56. formatRoute(row) {
  57. const from = row.senderCity || row.senderDistrict || row.senderProvince || "";
  58. const to = row.receiverCity || row.receiverDistrict || row.receiverProvince || "";
  59. return from && to ? from + " \u2192 " + to : "-";
  60. },
  61. transportStatusText(row) {
  62. const audit = row.auditStatus;
  63. const now = Date.now();
  64. const depart = row.planDepartTime ? new Date(row.planDepartTime).getTime() : null;
  65. const arrive = row.planArriveTime ? new Date(row.planArriveTime).getTime() : null;
  66. if (audit === 0) return "\u8349\u7a3f";
  67. if (audit === 1) return "\u5f85\u5ba1\u6838";
  68. if (depart && now < depart) return "\u5df2\u786e\u8ba4";
  69. if (arrive && now < arrive) return "\u5728\u9014";
  70. if (arrive && now >= arrive) return "\u5df2\u5230\u8fbe";
  71. return "\u5df2\u901a\u8fc7";
  72. },
  73. transportStatusTag(row) {
  74. const text = this.transportStatusText(row);
  75. const map = { "\u8349\u7a3f": "info", "\u5f85\u5ba1\u6838": "warning", "\u5df2\u786e\u8ba4": "success", "\u5728\u9014": "success", "\u5df2\u5230\u8fbe": "success", "\u5df2\u901a\u8fc7": "success" };
  76. return map[text] || "info";
  77. },
  78. displayCurrentTemp(row) {
  79. if (row.currentTemp != null && row.currentTemp !== "") {
  80. return Number(row.currentTemp).toFixed(1) + "\u00b0C";
  81. }
  82. const max = row.tempMax != null ? Number(row.tempMax) : null;
  83. const min = row.tempMin != null ? Number(row.tempMin) : 0;
  84. const audit = row.auditStatus;
  85. const now = Date.now();
  86. const depart = row.planDepartTime ? new Date(row.planDepartTime).getTime() : null;
  87. const arrive = row.planArriveTime ? new Date(row.planArriveTime).getTime() : null;
  88. const inTransit = audit === 2 && depart && now > depart && arrive && now < arrive;
  89. let ratio = depart && arrive ? (now - depart) / (arrive - depart) : 0.5;
  90. ratio = Math.max(0, Math.min(1, ratio || 0.5));
  91. const temp = inTransit && ratio > 0.4 && max != null ? max + 2.2 : (min + (max || 6)) / 2;
  92. return Number(temp).toFixed(1) + "\u00b0C";
  93. },
  94. isTempOverLimit(row) {
  95. const max = row.tempMax != null ? Number(row.tempMax) : null;
  96. const min = row.tempMin != null ? Number(row.tempMin) : null;
  97. const cur = parseFloat(this.displayCurrentTemp(row));
  98. if (isNaN(cur)) return false;
  99. return (max != null && cur > max) || (min != null && cur < min);
  100. },
  101. '''
  102. DETAIL_ATTACHMENTS = r''' attachments() {
  103. const raw = this.detail.attachment;
  104. const base = this.formatDateTime(this.detail.createTime) || "";
  105. const baseUrl = process.env.VUE_APP_BASE_API || "";
  106. if (!raw) return [];
  107. return String(raw).split(",").filter(Boolean).map((url) => {
  108. const path = url.trim();
  109. const name = path.lastIndexOf("/") >= 0 ? path.substring(path.lastIndexOf("/") + 1) : path;
  110. return { name, url: path, fullUrl: path.startsWith("http") ? path : baseUrl + path, time: base };
  111. });
  112. },'''
  113. DETAIL_PREVIEW = r''' previewFile(file) {
  114. const url = file.fullUrl || file.url;
  115. if (url) window.open(url, "_blank");
  116. else this.$modal.msgInfo("\u9884\u89c8\uff1a" + file.name);
  117. },
  118. downloadFile(file) {
  119. const url = file.fullUrl || file.url;
  120. if (url) window.open(url, "_blank");
  121. else this.$modal.msgInfo("\u4e0b\u8f7d\uff1a" + file.name);
  122. }'''
  123. def patch_index():
  124. text = open(INDEX, encoding="utf-8").read()
  125. # table: replace between selection and ???? column
  126. m = re.search(
  127. r'<el-table border stripe.*?<el-table-column label="\u64cd\u4f5c"',
  128. text,
  129. re.DOTALL,
  130. )
  131. if not m:
  132. start = text.find('<el-table border stripe')
  133. end = text.find('class-name="small-padding fixed-width"')
  134. if start < 0 or end < 0:
  135. raise SystemExit("table block not found")
  136. end = text.rfind("<el-table-column", 0, end)
  137. text = text[:start] + TABLE_NEW.encode("utf-8").decode("unicode_escape") + "\n " + text[end:]
  138. else:
  139. block = m.group(0)
  140. new_block = TABLE_NEW.encode("utf-8").decode("unicode_escape") + "\n <el-table-column label=\"\u64cd\u4f5c\""
  141. text = text.replace(block, new_block, 1)
  142. if "file-upload" not in text:
  143. text = text.replace(
  144. 'import WaybillDetail from "./components/WaybillDetail";',
  145. 'import WaybillDetail from "./components/WaybillDetail";\nimport FileUpload from "@/components/FileUpload";',
  146. )
  147. text = text.replace(
  148. "components: { WaybillDetail },",
  149. "components: { WaybillDetail, FileUpload },",
  150. )
  151. if "\u8fd0\u5355\u9644\u4ef6" not in text:
  152. marker = " </el-row>\n </el-form>\n\n <div slot=\"footer\" class=\"dialog-footer\">"
  153. alt = " </el-row>\n </el-form>"
  154. insert = FORM_INSERT.encode("utf-8").decode("unicode_escape")
  155. if marker in text:
  156. text = text.replace(marker, insert + marker, 1)
  157. else:
  158. idx = text.rfind('label="\u9884\u4f30\u91cc\u7a0b')
  159. if idx < 0:
  160. idx = text.rfind("estimateDistance")
  161. end = text.find("</el-row>", idx)
  162. text = text[: end + len("</el-row>")] + insert + text[end + len("</el-row>") :]
  163. if "formatRoute(row)" not in text:
  164. text = text.replace(
  165. " canEdit(row) {",
  166. METHODS_INSERT.encode("utf-8").decode("unicode_escape") + " canEdit(row) {",
  167. 1,
  168. )
  169. if 'attachment: ""' not in text and "attachment:" not in text.split("reset()")[1][:800]:
  170. text = text.replace(
  171. 'remark: "",',
  172. 'remark: "",\n attachment: "",',
  173. 1,
  174. )
  175. text = text.replace(
  176. "planSignTime: undefined,",
  177. "planSignTime: undefined,\n currentTemp: undefined,",
  178. 1,
  179. )
  180. open(INDEX, "w", encoding="utf-8", newline="\n").write(text)
  181. print("patched index.vue")
  182. def patch_detail():
  183. text = open(DETAIL, encoding="utf-8").read()
  184. # replace attachments computed
  185. pat = r"attachments\(\) \{[\s\S]*?\n \},\n watch:"
  186. rep = DETAIL_ATTACHMENTS.encode("utf-8").decode("unicode_escape") + " watch:"
  187. if re.search(pat, text):
  188. text = re.sub(pat, rep, text, count=1)
  189. # replace preview/download
  190. pat2 = r"previewFile\(file\) \{[\s\S]*?downloadFile\(file\) \{[\s\S]*?\n \}\n \}\n\};"
  191. rep2 = DETAIL_PREVIEW.encode("utf-8").decode("unicode_escape") + "\n }\n};"
  192. if re.search(pat2, text):
  193. text = re.sub(pat2, rep2, text, count=1)
  194. open(DETAIL, "w", encoding="utf-8", newline="\n").write(text)
  195. print("patched WaybillDetail.vue")
  196. if __name__ == "__main__":
  197. patch_index()
  198. patch_detail()