华西海圻ELN前端工程
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

493 lines
19 KiB

2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
  1. <template>
  2. <div>
  3. <LineLabel v-if="label" :label = "label"/>
  4. <div v-for="(item, index) in formConfig" :key="index">
  5. <template v-if="item.type === 'cardItem'">
  6. <div class="grid-container">
  7. <div v-for="(sItem, key) in item.config" class="form-item" :class="sItem.span == 1 ? 'full-row' : ''"
  8. :key="key">
  9. <template v-if="sItem.type === 'input'">
  10. <div class="form-title">{{ sItem.label }}</div>
  11. <HandleFormItem @blur="onBlur(key, $event)" :item="sItem" v-model="formFields[key]"
  12. @copy="onCopy(sItem, key)" />
  13. </template>
  14. <template v-else-if="sItem.type === 'inputNumber'">
  15. <div class="form-title">{{ sItem.label }}</div>
  16. <HandleFormItem type = "inputNumber" @blur="onBlur(key, $event)" :item="sItem" @input = "onInputNumberChange(key, $event)" v-model="formFields[key]"
  17. @copy="onCopy(sItem, key)" />
  18. </template>
  19. </div>
  20. </div>
  21. </template>
  22. <template v-else-if="item.type === 'conditionItem'">
  23. <div class="form-item ">
  24. <div class="form-title fs-16" v-if="item.label">{{ item.label }}</div>
  25. <div v-for="(sItem, key) in item.config" class="c-Item grid-container">
  26. <div class="p-r-20">
  27. <div class="form-title">{{ sItem.label }}</div>
  28. <div class="flex ">
  29. <HandleFormItem type="select" :item="sItem" v-model="formFields[key]"
  30. @copy="onCopy(sItem, key)" @change="onSelectChange(key, $event)" />
  31. </div>
  32. </div>
  33. <div class="p-l-20">
  34. <div v-show="isShowOther(formFields[key])">
  35. <div class="form-title">其他</div>
  36. <div class="flex">
  37. <HandleFormItem @blur="onBlur(key, $event)" :item="getOtherItem(sItem)" v-model="formFields[sItem.otherCode]"
  38. @copy="onCopy(sItem, key)" />
  39. </div>
  40. </div>
  41. </div>
  42. </div>
  43. </div>
  44. </template>
  45. <template v-else-if="item.type === 'cellItem'">
  46. <div class="form-item ">
  47. <div class="form-title fs-16" v-if="item.label">{{ item.label }}</div>
  48. <div class="grid-container gap2">
  49. <div v-for="(sItem, key) in item.config" class="c-Item" :class="getSpanClass(sItem)"
  50. :key="key">
  51. <div class="form-title" v-if="sItem.label">{{ sItem.label }}</div>
  52. <div v-if="sItem.type === 'dateTime'" class="flex1">
  53. <HandleFormItem type="dateTime" :item="sItem" v-model="formFields[key]"
  54. @copy="onCopy(sItem, key)" />
  55. </div>
  56. <div v-else-if="sItem.type === 'select'">
  57. <HandleFormItem type="select" :item="sItem" v-model="formFields[key]"
  58. @copy="onCopy(sItem, key)" @change="onSelectChange(key, $event)" />
  59. </div>
  60. <div v-else-if="sItem.type === 'input'">
  61. <HandleFormItem @blur="onBlur(key, $event)" :item="sItem" v-model="formFields[key]"
  62. @copy="onCopy(sItem, key)" />
  63. </div>
  64. <div v-else-if="sItem.type === 'textarea'">
  65. <HandleFormItem @blur="onBlur(key, $event)" type="textarea" :item="sItem" v-model="formFields[key]"
  66. @copy="onCopy(sItem, key)" />
  67. </div>
  68. </div>
  69. </div>
  70. </div>
  71. </template>
  72. <template v-else-if="item.type === 'step'">
  73. <div class="grid-container gap2">
  74. <div v-for="(sItem, key) in item.config" class="c-Item flex item-center" :class="getSpanClass(sItem)"
  75. :key="key">
  76. <div class="step-form-title" v-if="sItem.label">{{ sItem.label }}</div>
  77. <div v-if="sItem.type === 'dateTime'" class="flex1">
  78. <HandleFormItem type="dateTime" :item="sItem" v-model="formFields[key]"
  79. @copy="onCopy(sItem, key)" />
  80. </div>
  81. <div v-else-if="sItem.type === 'select'" class="flex flex1">
  82. <HandleFormItem type="select" :item="sItem" style="width: auto;flex:1" v-model="formFields[key]"
  83. @copy="onCopy(sItem, key)" @change="onSelectChange(key, $event)" />
  84. <div v-show="isShowOther(formFields[key])" class="flex flex1">
  85. <div class="other-title">其他</div>
  86. <div class="flex">
  87. <HandleFormItem @blur="onBlur(key, $event)" class="sub-select" :item="getOtherItem(sItem)" v-model="formFields[sItem.otherCode]"
  88. @copy="onCopy(sItem, key)" />
  89. </div>
  90. </div>
  91. </div>
  92. <div v-else-if="sItem.type === 'input'" class="flex flex1">
  93. <HandleFormItem @blur="onBlur(key, $event)" class="flex1" :item="sItem" v-model="formFields[key]"
  94. @copy="onCopy(sItem, key)" />
  95. <HandleFormItem v-if="sItem.subType === 'select'" type="select" class="sub-select" :item="getSubItem(sItem)" v-model="formFields[sItem.subKey]"
  96. @copy="onCopy(sItem, key)" @change="onSelectChange(sItem.subKey, $event)" />
  97. <div v-else-if="sItem.subType === 'span'">{{ formFields[sItem.subKey] }}</div>
  98. <HandleFormItem v-else-if="sItem.subType === 'clickable'" type="clickable" @clickable="handleClickable(sItem,$event)" class="sub-select" :item="getClickableItem(sItem)" :value="formFields[sItem.subKey]"
  99. />
  100. <!-- <div class="clickable" :class="getFillType(sItem.subFillType)" v-else-if = "sItem.subType ==='clickable'" @click="handleClickable(sItem,$event)">
  101. <span v-if="formFields[sItem.subKey]">{{ formFields[sItem.subKey] }}</span>
  102. <span v-else class="default-placeholder-text">请选择</span>
  103. </div> -->
  104. </div>
  105. <div v-else-if="sItem.type === 'inputNumber'" class="flex flex1">
  106. <HandleFormItem type = "inputNumber" @blur="onBlur(key, $event)" class="flex1" :item="sItem" @input = "onInputNumberChange(key, $event)" :value = "formFields[key]"
  107. @copy="onCopy(sItem, key)" />
  108. <HandleFormItem v-if="sItem.subType === 'select'" type="select" class="sub-select" :item="getSubItem(sItem)" v-model="formFields[sItem.subKey]"
  109. @copy="onCopy(sItem, key)" @change="onSelectChange(sItem.subKey, $event)" />
  110. <div v-else-if="sItem.subType === 'span'">{{ formFields[sItem.subKey] }}</div>
  111. <HandleFormItem v-else-if="sItem.subType === 'clickable'" @clickable="handleClickable(sItem,$event)" type="clickable" class="sub-select" :item="getClickableItem(sItem)" :value="formFields[sItem.subKey]"
  112. />
  113. <!-- <div class="clickable" :class="getFillType(sItem.subFillType)" v-else-if = "sItem.subType ==='clickable'" @click="handleClickable(sItem,$event)">
  114. <span v-if="formFields[sItem.subKey]">{{ formFields[sItem.subKey] }}</span>
  115. <span v-else class="default-placeholder-text">请选择</span>
  116. </div> -->
  117. </div>
  118. </div>
  119. </div>
  120. </template>
  121. </div>
  122. </div>
  123. </template>
  124. <script>
  125. import HandleFormItem from "./HandleFormItem.vue";
  126. import LineLabel from "./LineLabel.vue";
  127. export default {
  128. components: {
  129. HandleFormItem,
  130. LineLabel
  131. },
  132. props: {
  133. label:{//当前表单的标题
  134. type: String,
  135. default: "",
  136. },
  137. formConfig: {
  138. type: Array,
  139. value: () => [],
  140. },
  141. formData: {
  142. type: Object,
  143. value: () => ({})
  144. }
  145. },
  146. data() {
  147. return {
  148. formFields: {},//表单绑定字段
  149. allFieldsConfig: {},//包含config的所有字段,主要用于校验表单是否填写
  150. };
  151. },
  152. watch: {
  153. formData: {
  154. immediate: true,
  155. deep: true, // 深度监听,以便检测嵌套对象变化
  156. handler(v) {
  157. if(v){
  158. this.handleFormField();
  159. }
  160. }
  161. },
  162. formConfig: {
  163. immediate: true,
  164. deep: true, // 深度监听,以便检测嵌套对象变化
  165. handler(v) {
  166. this.handleFormField();
  167. }
  168. }
  169. },
  170. mounted() {
  171. this.handleFormField();
  172. },
  173. methods: {
  174. getFillType(type) {
  175. const typeObj = {
  176. actFill: "orange-border",//实际填写的边框颜色
  177. green: "green-border",
  178. preFill: "blue-border",//预填写的边框颜色
  179. }
  180. return typeObj[type] || ""
  181. },
  182. onInputNumberChange(key, val){
  183. this.formFields[key] = val;
  184. },
  185. updateFormData(key, value){
  186. this.formFields[key] = value;
  187. },
  188. batchUpdateFormData(data){
  189. Object.keys(data).forEach(key => {
  190. this.formFields[key] = data[key];
  191. })
  192. },
  193. handleClickable(sItem,event){
  194. if(this.fillType !== 'actFill'){
  195. return
  196. }
  197. this.$emit("clickable",sItem)
  198. },
  199. //根据span判断一行显示几列
  200. getSpanClass(sItem){
  201. const spanArr = ["full-row","","three-row"]
  202. if(sItem.span){
  203. return spanArr[sItem.span-1]
  204. }
  205. return ""
  206. },
  207. //获取其他下拉框的配置
  208. getOtherItem(sItem){
  209. return {
  210. label:"其他",
  211. fillType: sItem.fillType,
  212. maxlength: sItem.otherMaxlength || 50,
  213. }
  214. },
  215. getClickableItem(sItem){
  216. return {
  217. label: "",
  218. type: "clickable",
  219. fillType: sItem.subFillType || sItem.fillType,
  220. }
  221. },
  222. getSubItem(sItem){
  223. return {
  224. label: "",
  225. options: sItem.subOptions || [],
  226. fillType: sItem.subFillType || sItem.fillType,
  227. }
  228. },
  229. isShowOther(v = []) {
  230. // 确保v是数组类型,以避免类型错误
  231. const arr = Array.isArray(v) ? v : [v];
  232. //和凡哥商量,只要value为负数都显示其他
  233. return arr.some(item => item<0);
  234. },
  235. // 根据formConfig回填form表单数据
  236. handleFormField() {
  237. const result = {};
  238. let config = {};
  239. const { formConfig, formData, formFields } = this;
  240. // 遍历配置
  241. formConfig.forEach((item) => {
  242. if (item.config) {
  243. // 合并配置项
  244. config = { ...config, ...item.config }
  245. // 处理每个配置项
  246. Object.keys(item.config).forEach(key => {
  247. const currentConfig = item.config[key];
  248. let value = formData[key];
  249. // 如果formFields中已经有值,保持原值(用户输入或之前设置的值)
  250. if (formFields[key] !== null &&
  251. formFields[key] !== undefined &&
  252. formFields[key] !== ''&&
  253. typeof formFields[key] !== 'object'
  254. ) {
  255. console.log(key,formData,formFields[key],"kkk")
  256. // 保留原值,不使用formData中的值
  257. result[key] = formFields[key];
  258. } else {
  259. // 使用formData中的值
  260. result[key] = value;
  261. }
  262. // 处理特殊字段 - "其他"字段
  263. if (currentConfig.otherCode) {
  264. const { otherCode } = currentConfig;
  265. result[otherCode] = formData[otherCode] || '';
  266. config[otherCode] = { label: "其他", type: "input" }
  267. }
  268. if (currentConfig.subKey) {
  269. const { subKey } = currentConfig;
  270. result[subKey] = formData[subKey] || '';
  271. config[subKey] = { label: currentConfig.label, type: currentConfig.subType }
  272. }
  273. });
  274. // 处理可能存在的直接otherCode字段
  275. if (item.config?.otherCode) {
  276. config[item.config?.otherCode] = item.config?.otherCode;
  277. }
  278. }
  279. });
  280. console.log(result,"initResult")
  281. // 更新表单字段
  282. this.formFields = result;
  283. this.allFieldsConfig = config;
  284. console.log(config,"config")
  285. },
  286. //判断是否禁用
  287. getDisabled() {
  288. const { item } = this;
  289. const { fillType } = item;
  290. if (item.hasOwnProperty("disabled")) {
  291. return item.disabled
  292. } else {
  293. const { templateStatus } = this.$store.state.template;
  294. if (fillType === "actFill") {//当模板状态是实际填写时,只有当fillType是actFill时才能填写
  295. return templateStatus !== "actFill"
  296. } else if (fillType === "preFill") {//当模板状态是预填写时,只有当fillType是preFill才能填写
  297. return templateStatus !== "preFill"
  298. } else {
  299. return true
  300. }
  301. }
  302. },
  303. getFormData() {
  304. const { formFields, allFieldsConfig } = this;
  305. const { templateStatus } = this.$store.state.template;
  306. return new Promise((resolve,reject)=>{
  307. for (const key in formFields) {
  308. console.log(key,formFields[key])
  309. if (!formFields[key]) {
  310. const o = allFieldsConfig[key];
  311. if (o.fillType == templateStatus && !o.disabled) {
  312. let prefix = "";
  313. if (o.type === "input" || o.type === "inputNumber" || o.type === "textarea") {
  314. prefix = "填写"
  315. } else {
  316. prefix = "选择"
  317. }
  318. this.$message.error(`${o.label}还未${prefix}${prefix}后再提交`);
  319. reject(`${o.label}还未${prefix}`);
  320. return;
  321. }
  322. }
  323. }
  324. resolve(formFields)
  325. })
  326. },
  327. getFormDataByKey(key){
  328. return this.formFields[key];
  329. },
  330. onBlur(key, val) {
  331. this.$emit("blur", { key, value: val ,...this.formFields});
  332. },
  333. onSelectChange(key, val) {
  334. // 获取对应的配置
  335. const currentConfig = this.allFieldsConfig[key];
  336. // // 确保多选下拉框的值是数组类型
  337. // if (currentConfig && currentConfig.multiple) {
  338. // // 多选情况,确保值为数组类型
  339. // this.formFields[key] = Array.isArray(val) ? val : (val ? [val] : []);
  340. // } else {
  341. // 单选情况
  342. this.formFields[key] = val;
  343. // }
  344. this.$emit("select", { key, value: val });
  345. },
  346. //复制
  347. onCopy(config, key) {
  348. const { formFields } = this;
  349. if (config.copyFrom) {
  350. formFields[key] = formFields[config.copyFrom]
  351. }
  352. },
  353. },
  354. }
  355. </script>
  356. <style lang="scss">
  357. .grid-container {
  358. display: grid;
  359. grid-template-columns: repeat(2, 1fr);
  360. /* 默认2列 */
  361. gap: 0 20px;
  362. }
  363. .gap2{
  364. gap:0 64px;
  365. }
  366. .w-100 {
  367. width: 100%;
  368. }
  369. .form-item {
  370. background: #fff;
  371. padding: 20px;
  372. border-radius: 8px;
  373. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
  374. margin-top: 20px;
  375. padding: 20px;
  376. border-radius: 5px 5px;
  377. }
  378. /* 或者使用 span 语法 */
  379. .full-row {
  380. grid-column: span 2;
  381. }
  382. .three-row {
  383. grid-column: span 3;
  384. }
  385. .c-Item {
  386. &:not(:last-child) {
  387. margin-bottom: 16px;
  388. }
  389. }
  390. .eo{
  391. &:nth-child(even) {
  392. padding-left: 20px;
  393. }
  394. &:nth-child(odd) {
  395. padding-right: 20px;
  396. }
  397. }
  398. .default-placeholder-text{
  399. color: #C0C4CC;
  400. }
  401. .form-title {
  402. margin-bottom: 12px;
  403. font-size: 14px;
  404. font-weight: normal;
  405. color: #606266;
  406. }
  407. .step-form-title{
  408. font-size: 14px;
  409. font-weight: normal;
  410. color: #606266;
  411. width: 150px;
  412. text-align: right;
  413. padding-right: 10px;
  414. }
  415. .p-r-20{
  416. padding-right: 20px;
  417. }
  418. .p-l-20{
  419. padding-left: 20px;
  420. }
  421. .fs-16 {
  422. font-size: 0.96rem;
  423. font-weight: bold;
  424. color: #464647
  425. }
  426. .flex1 {
  427. flex: 1;
  428. }
  429. .flex {
  430. display: flex;
  431. }
  432. .other-title{
  433. width: 50px;
  434. text-align: right;
  435. margin: 0 10px;
  436. }
  437. .mr-24 {
  438. margin-right: 24px;
  439. }
  440. .sub-select{
  441. width: auto;
  442. margin-left: 10px;
  443. }
  444. .clickable{
  445. cursor: pointer;
  446. width: auto;
  447. margin-left: 10px;
  448. min-width: 100px;
  449. height: 28px;
  450. border-radius: 4px;
  451. border:1px solid #4ea2ff;
  452. display: flex;
  453. align-items: center;
  454. justify-content: center;
  455. font-size: 14px;
  456. font-weight: normal;
  457. color: #606266;
  458. }
  459. .orange-border {
  460. border-color: #f9c588;
  461. }
  462. .green-border {
  463. border-color: green;
  464. }
  465. .blue-border {
  466. border-color: #4ea2ff;
  467. }
  468. </style>