RuleEditDialog.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <template>
  2. <el-dialog
  3. :title="dialogTitle"
  4. v-model="innerVisible"
  5. width="640px"
  6. :close-on-click-modal="false"
  7. @close="handleCancel"
  8. >
  9. <el-form
  10. ref="form"
  11. :model="form"
  12. label-width="140px"
  13. label-position="right"
  14. >
  15. <el-form-item label="元标签规则" required>
  16. <el-select
  17. v-model="form.metaCode"
  18. placeholder="请选择"
  19. :disabled="readonly"
  20. style="width: 100%"
  21. @change="handleMetaChange"
  22. >
  23. <el-option
  24. v-for="meta in metaRules"
  25. :key="meta.code"
  26. :label="`${meta.code}-${meta.name}`"
  27. :value="meta.code"
  28. />
  29. </el-select>
  30. </el-form-item>
  31. <template v-if="currentMeta">
  32. <el-divider />
  33. <el-form-item label="规则名称">
  34. <el-input
  35. v-model="form.ruleName"
  36. :disabled="readonly"
  37. placeholder="便于识别该规则用途"
  38. maxlength="40"
  39. show-word-limit
  40. style="width: 100%"
  41. />
  42. </el-form-item>
  43. <el-form-item
  44. v-for="field in currentMeta.fields"
  45. :key="field.key"
  46. :label="field.label"
  47. :required="field.required"
  48. >
  49. <!-- 单选 -->
  50. <el-select
  51. v-if="field.type === 'select'"
  52. v-model="form.config[field.key]"
  53. :disabled="readonly"
  54. placeholder="请选择"
  55. clearable
  56. style="width: 100%"
  57. >
  58. <el-option
  59. v-for="opt in field.options"
  60. :key="String(opt.value)"
  61. :label="opt.label"
  62. :value="opt.value"
  63. />
  64. </el-select>
  65. <!-- 多选 -->
  66. <el-select
  67. v-else-if="field.type === 'multiSelect'"
  68. v-model="form.config[field.key]"
  69. :disabled="readonly"
  70. multiple
  71. collapse-tags
  72. placeholder="请选择"
  73. clearable
  74. style="width: 100%"
  75. >
  76. <el-option
  77. v-for="opt in field.options"
  78. :key="String(opt.value)"
  79. :label="opt.label"
  80. :value="opt.value"
  81. />
  82. </el-select>
  83. <!-- 日期 -->
  84. <el-date-picker
  85. v-else-if="field.type === 'datePicker'"
  86. v-model="form.config[field.key]"
  87. :disabled="readonly"
  88. type="date"
  89. placeholder="请选择日期"
  90. value-format="yyyy-MM-dd"
  91. style="width: 100%"
  92. />
  93. <!-- 月份 -->
  94. <el-date-picker
  95. v-else-if="field.type === 'monthPicker'"
  96. v-model="form.config[field.key]"
  97. :disabled="readonly"
  98. type="month"
  99. placeholder="请选择日期"
  100. value-format="yyyy-MM"
  101. style="width: 100%"
  102. />
  103. <!-- 数字 -->
  104. <el-input
  105. v-else-if="field.type === 'number'"
  106. v-model.number="form.config[field.key]"
  107. :disabled="readonly"
  108. type="number"
  109. :min="field.min"
  110. :max="field.max"
  111. placeholder="请输入"
  112. style="width: 100%"
  113. />
  114. <!-- 连续达标月数 / 时间范围 -->
  115. <div v-else-if="field.type === 'nm'" class="nm-input">
  116. <el-input
  117. v-model.number="form.config[field.key].n"
  118. :disabled="readonly"
  119. type="number"
  120. :min="1"
  121. style="width: 90px"
  122. />
  123. <span class="nm-sep">/</span>
  124. <el-input
  125. v-model.number="form.config[field.key].m"
  126. :disabled="readonly"
  127. type="number"
  128. :min="1"
  129. style="width: 90px"
  130. />
  131. </div>
  132. <!-- 文本 -->
  133. <el-input
  134. v-else
  135. v-model="form.config[field.key]"
  136. :disabled="readonly"
  137. placeholder="请输入"
  138. style="width: 100%"
  139. />
  140. </el-form-item>
  141. </template>
  142. </el-form>
  143. <template #footer>
  144. <div class="dialog-footer">
  145. <el-button @click="handleCancel">取 消</el-button>
  146. <el-button v-if="!readonly" type="primary" @click="handleSave">保 存</el-button>
  147. </div>
  148. </template>
  149. </el-dialog>
  150. </template>
  151. <script>
  152. import { ElMessage } from 'element-plus'
  153. import { META_RULES, getMetaRule } from '@/config/metaRules'
  154. const emptyForm = () => ({
  155. id: '',
  156. metaCode: '',
  157. ruleName: '',
  158. config: {}
  159. })
  160. export default {
  161. name: 'RuleEditDialog',
  162. props: {
  163. modelValue: { type: Boolean, default: false },
  164. rule: { type: Object, default: null },
  165. mode: { type: String, default: 'add' } // add | edit | view
  166. },
  167. emits: ['update:modelValue', 'save', 'cancel'],
  168. data() {
  169. return {
  170. form: emptyForm(),
  171. metaRules: META_RULES
  172. }
  173. },
  174. computed: {
  175. innerVisible: {
  176. get() { return this.modelValue },
  177. set(v) { this.$emit('update:modelValue', v) }
  178. },
  179. dialogTitle() {
  180. if (this.mode === 'edit') return '编辑规则信息'
  181. if (this.mode === 'view') return '查看规则信息'
  182. return '新增规则信息'
  183. },
  184. readonly() {
  185. return this.mode === 'view'
  186. },
  187. currentMeta() {
  188. return this.form.metaCode ? getMetaRule(this.form.metaCode) : null
  189. }
  190. },
  191. watch: {
  192. modelValue(v) {
  193. if (v) this.initForm()
  194. }
  195. },
  196. methods: {
  197. initForm() {
  198. if (this.rule) {
  199. this.form = {
  200. id: this.rule.id,
  201. metaCode: this.rule.metaCode,
  202. ruleName: this.rule.ruleName || '',
  203. config: JSON.parse(JSON.stringify(this.rule.config || {}))
  204. }
  205. } else {
  206. this.form = emptyForm()
  207. }
  208. },
  209. handleMetaChange(code) {
  210. const meta = getMetaRule(code)
  211. const config = {}
  212. if (meta) {
  213. meta.fields.forEach((f) => {
  214. if (f.type === 'multiSelect') config[f.key] = []
  215. else if (f.type === 'nm') config[f.key] = f.defaultValue ? { ...f.defaultValue } : { n: 3, m: 6 }
  216. else config[f.key] = ''
  217. })
  218. }
  219. this.form.config = config
  220. if (!this.form.ruleName && meta) {
  221. this.form.ruleName = meta.name
  222. }
  223. },
  224. validateConfig() {
  225. if (!this.form.metaCode) {
  226. ElMessage.error('请选择元标签规则')
  227. return false
  228. }
  229. const meta = this.currentMeta
  230. if (!meta) return false
  231. for (const f of meta.fields) {
  232. if (!f.required) continue
  233. const v = this.form.config[f.key]
  234. const empty = v === '' || v === null || v === undefined ||
  235. (Array.isArray(v) && v.length === 0)
  236. if (empty) {
  237. ElMessage.error(`请填写「${f.label}」`)
  238. return false
  239. }
  240. }
  241. return true
  242. },
  243. handleSave() {
  244. if (!this.validateConfig()) return
  245. this.$emit('save', {
  246. id: this.form.id || `r_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
  247. metaCode: this.form.metaCode,
  248. ruleName: this.form.ruleName || this.currentMeta.name,
  249. config: JSON.parse(JSON.stringify(this.form.config))
  250. })
  251. this.innerVisible = false
  252. },
  253. handleCancel() {
  254. this.innerVisible = false
  255. this.$emit('cancel')
  256. }
  257. }
  258. }
  259. </script>
  260. <style scoped>
  261. .nm-input { display: flex; align-items: center; }
  262. .nm-sep { margin: 0 12px; color: #303133; font-size: 16px; }
  263. .dialog-footer { text-align: center; }
  264. </style>