|
|
- <template>
- <div>
- <LineLabel v-if="label" :label = "label"/>
- <div v-for="(item, index) in formConfig" :key="index">
- <template v-if="item.type === 'cardItem'">
- <div class="grid-container">
- <div v-for="(sItem, key) in item.config" class="form-item" :class="sItem.span == 1 ? 'full-row' : ''"
- :key="key">
- <template v-if="sItem.type === 'input'">
- <div class="form-title">{{ sItem.label }}</div>
- <HandleFormItem @blur="onBlur(key, $event)" :item="sItem" v-model="formFields[key]"
- @copy="onCopy(sItem, key)" />
- </template>
- <template v-else-if="sItem.type === 'inputNumber'">
- <div class="form-title">{{ sItem.label }}</div>
- <HandleFormItem type = "inputNumber" @blur="onBlur(key, $event)" :item="sItem" @input = "onInputNumberChange(key, $event)" v-model="formFields[key]"
- @copy="onCopy(sItem, key)" />
- </template>
- </div>
- </div>
- </template>
- <template v-else-if="item.type === 'conditionItem'">
- <div class="form-item ">
- <div class="form-title fs-16" v-if="item.label">{{ item.label }}</div>
- <div v-for="(sItem, key) in item.config" class="c-Item grid-container">
- <div class="p-r-20">
- <div class="form-title">{{ sItem.label }}</div>
- <div class="flex ">
- <HandleFormItem type="select" :item="sItem" v-model="formFields[key]"
- @copy="onCopy(sItem, key)" @change="onSelectChange(key, $event)" />
-
- </div>
- </div>
- <div class="p-l-20">
- <div v-show="isShowOther(formFields[key])">
- <div class="form-title">其他</div>
- <div class="flex">
- <HandleFormItem @blur="onBlur(key, $event)" :item="getOtherItem(sItem)" v-model="formFields[sItem.otherCode]"
- @copy="onCopy(sItem, key)" />
- </div>
- </div>
-
- </div>
-
- </div>
- </div>
- </template>
- <template v-else-if="item.type === 'cellItem'">
- <div class="form-item ">
- <div class="form-title fs-16" v-if="item.label">{{ item.label }}</div>
- <div class="grid-container gap2">
- <div v-for="(sItem, key) in item.config" class="c-Item" :class="getSpanClass(sItem)"
- :key="key">
- <div class="form-title" v-if="sItem.label">{{ sItem.label }}</div>
- <div v-if="sItem.type === 'dateTime'" class="flex1">
- <HandleFormItem type="dateTime" :item="sItem" v-model="formFields[key]"
- @copy="onCopy(sItem, key)" />
- </div>
- <div v-else-if="sItem.type === 'select'">
- <HandleFormItem type="select" :item="sItem" v-model="formFields[key]"
- @copy="onCopy(sItem, key)" @change="onSelectChange(key, $event)" />
- </div>
- <div v-else-if="sItem.type === 'input'">
- <HandleFormItem @blur="onBlur(key, $event)" :item="sItem" v-model="formFields[key]"
- @copy="onCopy(sItem, key)" />
- </div>
- <div v-else-if="sItem.type === 'textarea'">
- <HandleFormItem @blur="onBlur(key, $event)" type="textarea" :item="sItem" v-model="formFields[key]"
- @copy="onCopy(sItem, key)" />
- </div>
- </div>
- </div>
-
- </div>
- </template>
- <template v-else-if="item.type === 'step'">
- <div class="grid-container gap2">
- <div v-for="(sItem, key) in item.config" class="c-Item flex item-center" :class="getSpanClass(sItem)"
- :key="key">
- <div class="step-form-title" v-if="sItem.label">{{ sItem.label }}</div>
- <div v-if="sItem.type === 'dateTime'" class="flex1">
- <HandleFormItem type="dateTime" :item="sItem" v-model="formFields[key]"
- @copy="onCopy(sItem, key)" />
- </div>
- <div v-else-if="sItem.type === 'select'" class="flex flex1">
- <HandleFormItem type="select" :item="sItem" style="width: auto;flex:1" v-model="formFields[key]"
- @copy="onCopy(sItem, key)" @change="onSelectChange(key, $event)" />
- <div v-show="isShowOther(formFields[key])" class="flex flex1">
- <div class="other-title">其他</div>
- <div class="flex">
- <HandleFormItem @blur="onBlur(key, $event)" class="sub-select" :item="getOtherItem(sItem)" v-model="formFields[sItem.otherCode]"
- @copy="onCopy(sItem, key)" />
- </div>
- </div>
- </div>
- <div v-else-if="sItem.type === 'input'" class="flex flex1">
- <HandleFormItem @blur="onBlur(key, $event)" class="flex1" :item="sItem" v-model="formFields[key]"
- @copy="onCopy(sItem, key)" />
- <HandleFormItem v-if="sItem.subType === 'select'" type="select" class="sub-select" :item="getSubItem(sItem)" v-model="formFields[sItem.subKey]"
- @copy="onCopy(sItem, key)" @change="onSelectChange(sItem.subKey, $event)" />
- <div v-else-if="sItem.subType === 'span'">{{ formFields[sItem.subKey] }}</div>
- <HandleFormItem v-else-if="sItem.subType === 'clickable'" type="clickable" @clickable="handleClickable(sItem,$event)" class="sub-select" :item="getClickableItem(sItem)" :value="formFields[sItem.subKey]"
- />
- <!-- <div class="clickable" :class="getFillType(sItem.subFillType)" v-else-if = "sItem.subType ==='clickable'" @click="handleClickable(sItem,$event)">
- <span v-if="formFields[sItem.subKey]">{{ formFields[sItem.subKey] }}</span>
- <span v-else class="default-placeholder-text">请选择</span>
- </div> -->
- </div>
- <div v-else-if="sItem.type === 'inputNumber'" class="flex flex1">
- <HandleFormItem type = "inputNumber" @blur="onBlur(key, $event)" class="flex1" :item="sItem" @input = "onInputNumberChange(key, $event)" :value = "formFields[key]"
- @copy="onCopy(sItem, key)" />
- <HandleFormItem v-if="sItem.subType === 'select'" type="select" class="sub-select" :item="getSubItem(sItem)" v-model="formFields[sItem.subKey]"
- @copy="onCopy(sItem, key)" @change="onSelectChange(sItem.subKey, $event)" />
- <div v-else-if="sItem.subType === 'span'">{{ formFields[sItem.subKey] }}</div>
- <HandleFormItem v-else-if="sItem.subType === 'clickable'" @clickable="handleClickable(sItem,$event)" type="clickable" class="sub-select" :item="getClickableItem(sItem)" :value="formFields[sItem.subKey]"
- />
- <!-- <div class="clickable" :class="getFillType(sItem.subFillType)" v-else-if = "sItem.subType ==='clickable'" @click="handleClickable(sItem,$event)">
- <span v-if="formFields[sItem.subKey]">{{ formFields[sItem.subKey] }}</span>
- <span v-else class="default-placeholder-text">请选择</span>
- </div> -->
- </div>
- </div>
- </div>
- </template>
- </div>
- </div>
- </template>
-
- <script>
- import HandleFormItem from "./HandleFormItem.vue";
- import LineLabel from "./LineLabel.vue";
- export default {
- components: {
- HandleFormItem,
- LineLabel
- },
- props: {
- label:{//当前表单的标题
- type: String,
- default: "",
- },
- formConfig: {
- type: Array,
- value: () => [],
- },
- formData: {
- type: Object,
- value: () => ({})
- }
- },
- data() {
- return {
- formFields: {},//表单绑定字段
- allFieldsConfig: {},//包含config的所有字段,主要用于校验表单是否填写
- };
- },
- watch: {
- formData: {
- immediate: true,
- deep: true, // 深度监听,以便检测嵌套对象变化
- handler(v) {
- if(v){
- this.handleFormField();
- }
- }
- },
- formConfig: {
- immediate: true,
- deep: true, // 深度监听,以便检测嵌套对象变化
- handler(v) {
- this.handleFormField();
- }
- }
- },
- mounted() {
- this.handleFormField();
- },
-
- methods: {
- getFillType(type) {
- const typeObj = {
- actFill: "orange-border",//实际填写的边框颜色
- green: "green-border",
- preFill: "blue-border",//预填写的边框颜色
- }
- return typeObj[type] || ""
- },
- onInputNumberChange(key, val){
- this.formFields[key] = val;
- },
- updateFormData(key, value){
- this.formFields[key] = value;
- },
- batchUpdateFormData(data){
- Object.keys(data).forEach(key => {
- this.formFields[key] = data[key];
- })
- },
- handleClickable(sItem,event){
- if(this.fillType !== 'actFill'){
- return
- }
- this.$emit("clickable",sItem)
- },
- //根据span判断一行显示几列
- getSpanClass(sItem){
- const spanArr = ["full-row","","three-row"]
- if(sItem.span){
- return spanArr[sItem.span-1]
- }
- return ""
- },
- //获取其他下拉框的配置
- getOtherItem(sItem){
- return {
- label:"其他",
- fillType: sItem.fillType,
- maxlength: sItem.otherMaxlength || 50,
- }
- },
- getClickableItem(sItem){
- return {
- label: "",
- type: "clickable",
- fillType: sItem.subFillType || sItem.fillType,
- }
- },
- getSubItem(sItem){
- return {
- label: "",
- options: sItem.subOptions || [],
- fillType: sItem.subFillType || sItem.fillType,
- }
- },
- isShowOther(v = []) {
- // 确保v是数组类型,以避免类型错误
- const arr = Array.isArray(v) ? v : [v];
- //和凡哥商量,只要value为负数都显示其他
- return arr.some(item => item<0);
- },
- // 根据formConfig回填form表单数据
- handleFormField() {
- const result = {};
- let config = {};
- const { formConfig, formData, formFields } = this;
- // 遍历配置
- formConfig.forEach((item) => {
- if (item.config) {
- // 合并配置项
- config = { ...config, ...item.config }
-
- // 处理每个配置项
- Object.keys(item.config).forEach(key => {
- const currentConfig = item.config[key];
- let value = formData[key];
-
- // 如果formFields中已经有值,保持原值(用户输入或之前设置的值)
- if (formFields[key] !== null &&
- formFields[key] !== undefined &&
- formFields[key] !== ''&&
- typeof formFields[key] !== 'object'
- ) {
- console.log(key,formData,formFields[key],"kkk")
- // 保留原值,不使用formData中的值
- result[key] = formFields[key];
- } else {
- // 使用formData中的值
- result[key] = value;
- }
-
- // 处理特殊字段 - "其他"字段
- if (currentConfig.otherCode) {
- const { otherCode } = currentConfig;
- result[otherCode] = formData[otherCode] || '';
- config[otherCode] = { label: "其他", type: "input" }
- }
- if (currentConfig.subKey) {
- const { subKey } = currentConfig;
- result[subKey] = formData[subKey] || '';
- config[subKey] = { label: currentConfig.label, type: currentConfig.subType }
- }
- });
-
- // 处理可能存在的直接otherCode字段
- if (item.config?.otherCode) {
- config[item.config?.otherCode] = item.config?.otherCode;
- }
- }
- });
- console.log(result,"initResult")
- // 更新表单字段
- this.formFields = result;
- this.allFieldsConfig = config;
- console.log(config,"config")
- },
- //判断是否禁用
- getDisabled() {
- const { item } = this;
- const { fillType } = item;
- if (item.hasOwnProperty("disabled")) {
- return item.disabled
- } else {
- const { templateStatus } = this.$store.state.template;
- if (fillType === "actFill") {//当模板状态是实际填写时,只有当fillType是actFill时才能填写
- return templateStatus !== "actFill"
- } else if (fillType === "preFill") {//当模板状态是预填写时,只有当fillType是preFill才能填写
- return templateStatus !== "preFill"
- } else {
- return true
- }
- }
- },
- getFormData() {
- const { formFields, allFieldsConfig } = this;
- const { templateStatus } = this.$store.state.template;
- return new Promise((resolve,reject)=>{
- for (const key in formFields) {
- console.log(key,formFields[key])
- if (!formFields[key]) {
- const o = allFieldsConfig[key];
- if (o.fillType == templateStatus && !o.disabled) {
- let prefix = "";
- if (o.type === "input" || o.type === "inputNumber" || o.type === "textarea") {
- prefix = "填写"
- } else {
- prefix = "选择"
- }
- this.$message.error(`${o.label}还未${prefix}请${prefix}后再提交`);
- reject(`${o.label}还未${prefix}`);
- return;
- }
-
- }
- }
- resolve(formFields)
- })
-
- },
- getFormDataByKey(key){
- return this.formFields[key];
- },
- onBlur(key, val) {
- this.$emit("blur", { key, value: val ,...this.formFields});
- },
- onSelectChange(key, val) {
- // 获取对应的配置
- const currentConfig = this.allFieldsConfig[key];
-
- // // 确保多选下拉框的值是数组类型
- // if (currentConfig && currentConfig.multiple) {
- // // 多选情况,确保值为数组类型
- // this.formFields[key] = Array.isArray(val) ? val : (val ? [val] : []);
- // } else {
- // 单选情况
- this.formFields[key] = val;
- // }
- this.$emit("select", { key, value: val });
- },
- //复制
- onCopy(config, key) {
- const { formFields } = this;
- if (config.copyFrom) {
- formFields[key] = formFields[config.copyFrom]
- }
- },
- },
- }
- </script>
-
- <style lang="scss">
- .grid-container {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- /* 默认2列 */
- gap: 0 20px;
- }
- .gap2{
- gap:0 64px;
- }
-
- .w-100 {
- width: 100%;
- }
-
- .form-item {
- background: #fff;
- padding: 20px;
- border-radius: 8px;
- box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
- margin-top: 20px;
- padding: 20px;
- border-radius: 5px 5px;
-
- }
-
- /* 或者使用 span 语法 */
- .full-row {
- grid-column: span 2;
- }
- .three-row {
- grid-column: span 3;
- }
-
-
- .c-Item {
- &:not(:last-child) {
- margin-bottom: 16px;
- }
- }
- .eo{
- &:nth-child(even) {
- padding-left: 20px;
- }
- &:nth-child(odd) {
- padding-right: 20px;
- }
- }
- .default-placeholder-text{
- color: #C0C4CC;
- }
- .form-title {
- margin-bottom: 12px;
- font-size: 14px;
- font-weight: normal;
- color: #606266;
- }
- .step-form-title{
- font-size: 14px;
- font-weight: normal;
- color: #606266;
- width: 150px;
- text-align: right;
- padding-right: 10px;
- }
- .p-r-20{
- padding-right: 20px;
- }
- .p-l-20{
- padding-left: 20px;
- }
-
- .fs-16 {
- font-size: 0.96rem;
- font-weight: bold;
- color: #464647
- }
-
- .flex1 {
- flex: 1;
- }
-
- .flex {
- display: flex;
- }
- .other-title{
- width: 50px;
- text-align: right;
- margin: 0 10px;
- }
- .mr-24 {
- margin-right: 24px;
- }
- .sub-select{
- width: auto;
- margin-left: 10px;
- }
- .clickable{
- cursor: pointer;
- width: auto;
- margin-left: 10px;
- min-width: 100px;
- height: 28px;
- border-radius: 4px;
- border:1px solid #4ea2ff;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 14px;
- font-weight: normal;
- color: #606266;
- }
-
- .orange-border {
- border-color: #f9c588;
- }
-
- .green-border {
- border-color: green;
- }
-
- .blue-border {
- border-color: #4ea2ff;
- }
- </style>
|