|
@@ -0,0 +1,1026 @@
|
|
|
+<template>
|
|
|
+ <main class="index_main">
|
|
|
+ <HomePageHead></HomePageHead>
|
|
|
+ <HomePageNavigation></HomePageNavigation>
|
|
|
+
|
|
|
+ <el-skeleton v-if="pageLoading" :rows="24" :animated="true" style="margin: 20px;">
|
|
|
+ <template #template>
|
|
|
+ <div class="custom-skeleton-multi">
|
|
|
+ <span
|
|
|
+ v-for="(item, idx) in skeletonChars"
|
|
|
+ :key="idx"
|
|
|
+ class="custom-skeleton-char"
|
|
|
+ :style="{
|
|
|
+ top: item.top + '%',
|
|
|
+ left: item.left + '%',
|
|
|
+ fontSize: item.fontSize + 'px',
|
|
|
+ opacity: item.opacity,
|
|
|
+ transform: `rotate(${item.rotate}deg)`
|
|
|
+ }"
|
|
|
+ >政讯通</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-skeleton>
|
|
|
+ <el-form v-else :model="formData" :rules="rules_js" ref="formRef" label-width="120px" class="form_box">
|
|
|
+ <el-form-item
|
|
|
+ v-for="(per_obj,per_index) in table_head.value"
|
|
|
+ :key="per_index"
|
|
|
+ :prop="getFieldName(per_obj)"
|
|
|
+ :label="per_obj.title + ':'"
|
|
|
+ :required="per_obj.is_check === 1"
|
|
|
+ >
|
|
|
+ <!-- 文本文字1 -->
|
|
|
+ <el-input
|
|
|
+ v-if="checkFieldType(per_obj, 1)"
|
|
|
+ v-model="formData[getFieldName(per_obj)]"
|
|
|
+ :placeholder="`请输入${per_obj.title}`"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 多行textarea 2-->
|
|
|
+ <el-input
|
|
|
+ v-if="checkFieldType(per_obj, 2)"
|
|
|
+ v-model="formData[getFieldName(per_obj)]"
|
|
|
+ type="textarea"
|
|
|
+ :autosize="{ minRows: 2, maxRows: 4 }"
|
|
|
+ :placeholder="`请输入${per_obj.title}`"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 单选按钮 3 -->
|
|
|
+ <el-radio-group
|
|
|
+ v-if="checkFieldType(per_obj, 3)"
|
|
|
+ v-model="formData[getFieldName(per_obj)]">
|
|
|
+ <el-radio
|
|
|
+ v-for="per_3_obj in radio_arr_fun(per_obj.option_value)"
|
|
|
+ :key="`${getFieldName(per_obj)}_${per_3_obj[1]}`"
|
|
|
+ :label="per_3_obj[1]">
|
|
|
+ {{ per_3_obj[0] }}
|
|
|
+ </el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+
|
|
|
+ <!-- 下拉选择 4 -->
|
|
|
+ <el-select
|
|
|
+ v-if="checkFieldType(per_obj, 4)"
|
|
|
+ placeholder="请选择"
|
|
|
+ v-model="formData[getFieldName(per_obj)]"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="per_4_obj in select_arr_fun(per_obj.option_value)"
|
|
|
+ :key="`${getFieldName(per_obj)}_${per_4_obj.value}`"
|
|
|
+ :label="per_4_obj.label"
|
|
|
+ :value="per_4_obj.value">
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+
|
|
|
+ <!-- 复选框5 -->
|
|
|
+ <el-checkbox-group
|
|
|
+ v-if="checkFieldType(per_obj, 5)"
|
|
|
+ v-model="formData[getFieldName(per_obj)]"
|
|
|
+ @change="(value) => handleCheckboxChange(getFieldName(per_obj), value)"
|
|
|
+ >
|
|
|
+ <el-checkbox
|
|
|
+ v-for="per_5_obj in checkbox_arr_fun(per_obj.option_value)"
|
|
|
+ :key="`${getFieldName(per_obj)}_${per_5_obj.value}`"
|
|
|
+ :label="per_5_obj.value">
|
|
|
+ {{ per_5_obj.label }}
|
|
|
+ </el-checkbox>
|
|
|
+ </el-checkbox-group>
|
|
|
+
|
|
|
+ <!-- 日期选择器 6 -->
|
|
|
+ <el-date-picker
|
|
|
+ v-if="checkFieldType(per_obj, 6)"
|
|
|
+ v-model="formData[getFieldName(per_obj)]"
|
|
|
+ type="date"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ format="YYYY-MM-DD"
|
|
|
+ placeholder="选择日期"
|
|
|
+ style="width: 100%">
|
|
|
+ </el-date-picker>
|
|
|
+ <!-- 单文件上传 7 (新) -->
|
|
|
+ <el-upload
|
|
|
+ v-if="checkFieldType(per_obj, 7)"
|
|
|
+ :ref="(el) => uploadRefs[getFieldName(per_obj)] = el"
|
|
|
+ :action="`${$webUrl}/public/uploadFile`"
|
|
|
+ :limit="1"
|
|
|
+ :on-exceed="(files) => handleExceed(files, getFieldName(per_obj))"
|
|
|
+ :on-success="(res, file) => handleFileSuccess(res, file, getFieldName(per_obj))"
|
|
|
+ :on-remove="() => handleFileRemove(getFieldName(per_obj))"
|
|
|
+ :file-list="fileLists[getFieldName(per_obj)] || []"
|
|
|
+ >
|
|
|
+ <el-button type="primary">点击上传文件</el-button>
|
|
|
+ <template #tip>
|
|
|
+ <div class="el-upload__tip">
|
|
|
+ 只能上传一个文件,新文件将覆盖旧文件。
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-upload>
|
|
|
+ <!-- img上传 8 -->
|
|
|
+ <el-upload
|
|
|
+ v-if="checkFieldType(per_obj, 8)"
|
|
|
+ class="avatar-uploader"
|
|
|
+ :action="`${$webUrl}/public/uploadFile`"
|
|
|
+ :show-file-list="false"
|
|
|
+ :on-success="(res) => handleAvatarSuccess(res, getFieldName(per_obj))"
|
|
|
+ :before-upload="beforeAvatarUpload"
|
|
|
+ >
|
|
|
+ <img v-if="formData[getFieldName(per_obj)]" :src="formData[getFieldName(per_obj)]" class="avatar" >
|
|
|
+ <div v-else class="avatar-uploader-placeholder">
|
|
|
+ <img src="http://img.bjzxtw.org.cn/master/image/noImage.png" class="avatar-placeholder-img" alt="上传头像">
|
|
|
+ <span class="upload-tip">点击上传</span>
|
|
|
+ </div>
|
|
|
+ </el-upload>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- 验证码 -->
|
|
|
+ <el-form-item v-if="showCaptcha" prop="code" label="验证码:">
|
|
|
+ <div class="captcha-wrapper">
|
|
|
+ <el-input v-model="formData.code" placeholder="请输入验证码" @keyup.enter="submitForm"></el-input>
|
|
|
+ <img v-if="captchaImage" :src="captchaImage" @click="refreshCaptcha" alt="验证码" class="captcha-img" title="点击刷新">
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item class="form-actions">
|
|
|
+ <el-button type="primary" @click="submitForm" :loading="submitLoading">提交</el-button>
|
|
|
+ <el-button @click="resetForm">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <HomeFoot></HomeFoot>
|
|
|
+ </main>
|
|
|
+ <el-dialog v-model="dialogVisible" title="提示" width="300px" :close-on-click-modal="false" :show-close="false" align-center>
|
|
|
+ <div style="text-align:center;">表单提交成功!</div>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer-center">
|
|
|
+ <el-button type="primary" @click="onDialogConfirm" class="dialog-confirm-btn">确认</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+@charset "utf-8";
|
|
|
+* {
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ font-family: "微软雅黑", "microsoft yahei";
|
|
|
+}
|
|
|
+
|
|
|
+ul,
|
|
|
+ol {
|
|
|
+ list-style: none;
|
|
|
+}
|
|
|
+
|
|
|
+a:active {
|
|
|
+ text-decoration: none;
|
|
|
+}
|
|
|
+
|
|
|
+a:hover {
|
|
|
+ text-decoration: none;
|
|
|
+}
|
|
|
+
|
|
|
+a:visited {
|
|
|
+ text-decoration: none;
|
|
|
+}
|
|
|
+
|
|
|
+a:link {
|
|
|
+ text-decoration: none;
|
|
|
+}
|
|
|
+
|
|
|
+a:focus {
|
|
|
+ text-decoration: none;
|
|
|
+}
|
|
|
+
|
|
|
+body {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.clearfix {
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.clearfix_2::after {
|
|
|
+ content: '';
|
|
|
+ display: block;
|
|
|
+ height: 0;
|
|
|
+ visibility: hidden;
|
|
|
+ clear: both;
|
|
|
+}
|
|
|
+
|
|
|
+.hiddenColor {
|
|
|
+ visibility: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.hand {
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.aTag_parent {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.aTag_parent>a,
|
|
|
+.aTag {
|
|
|
+ display: block;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ position: absolute;
|
|
|
+ z-index: 99;
|
|
|
+ border: 0px;
|
|
|
+ top: 0px;
|
|
|
+ left: 0px;
|
|
|
+ background: rgba(0, 0, 0, 0);
|
|
|
+}
|
|
|
+
|
|
|
+.dot1 {
|
|
|
+ display: block;
|
|
|
+ word-break: keep-all;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+
|
|
|
+.dot2 {
|
|
|
+ overflow: hidden;
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ -webkit-line-clamp: 2;
|
|
|
+}
|
|
|
+.dot3{
|
|
|
+ overflow: hidden;
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ -webkit-line-clamp: 3;
|
|
|
+}
|
|
|
+
|
|
|
+input,
|
|
|
+img {
|
|
|
+ border: none;
|
|
|
+}
|
|
|
+
|
|
|
+.cover100 img {
|
|
|
+ display: block;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+}
|
|
|
+
|
|
|
+.back100 {
|
|
|
+ background-size: 100% 100%;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+}
|
|
|
+
|
|
|
+article,
|
|
|
+aside,
|
|
|
+footer,
|
|
|
+header,
|
|
|
+time,
|
|
|
+video,
|
|
|
+main,
|
|
|
+nav,
|
|
|
+h4,
|
|
|
+h3,
|
|
|
+section {
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+.index_main {
|
|
|
+ margin: 0 auto;
|
|
|
+}
|
|
|
+
|
|
|
+.slow_6 {
|
|
|
+ -webkit-transition: all .6s;
|
|
|
+ -moz-transition: all .6s;
|
|
|
+ -ms-transition: all .6s;
|
|
|
+ -o-transition: all .6s;
|
|
|
+ transition: all .6s;
|
|
|
+}
|
|
|
+.form_box {
|
|
|
+ width: 1200px;
|
|
|
+ margin: 20px auto;
|
|
|
+ border: dashed 1px #000;
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.form_box :deep(.avatar-uploader) {
|
|
|
+ border: 1px dashed var(--el-border-color);
|
|
|
+ border-radius: 6px;
|
|
|
+ cursor: pointer;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+ width: 178px;
|
|
|
+ height: 178px;
|
|
|
+}
|
|
|
+
|
|
|
+.form_box :deep(.avatar-uploader:hover) {
|
|
|
+ border-color: var(--el-color-primary);
|
|
|
+}
|
|
|
+
|
|
|
+.form_box :deep(.avatar-uploader .avatar) {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+.form_box :deep(.avatar-uploader .el-upload) {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.form_box :deep(.el-button--primary) {
|
|
|
+ padding: 0 11px;
|
|
|
+}
|
|
|
+
|
|
|
+@media screen and (min-width:1200px) {
|
|
|
+ /*pc_1440*/
|
|
|
+ @media screen and (max-width:1440px) {
|
|
|
+ /*1200*/
|
|
|
+ }
|
|
|
+
|
|
|
+ .pc_none {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media screen and (max-width:599px) {}
|
|
|
+
|
|
|
+@media screen and (max-width:320px) {}
|
|
|
+
|
|
|
+.form_box .el-form-item {
|
|
|
+ margin-bottom: 20px!important;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar-uploader-placeholder {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar-placeholder-img {
|
|
|
+ width: 50px;
|
|
|
+ height: 50px;
|
|
|
+ opacity: 0.5;
|
|
|
+}
|
|
|
+
|
|
|
+.upload-tip {
|
|
|
+ margin-top: 8px;
|
|
|
+ color: #8c939d;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.form-actions :deep(.el-form-item__content) {
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.form-actions .el-button + .el-button {
|
|
|
+ margin-left: 20px;
|
|
|
+ padding: 0 11px;
|
|
|
+}
|
|
|
+
|
|
|
+.captcha-wrapper {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.captcha-img {
|
|
|
+ margin-left: 10px;
|
|
|
+ cursor: pointer;
|
|
|
+ height: 32px; /* 与el-input默认高度对齐 */
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.form_box :deep(.el-checkbox) {
|
|
|
+ margin-right: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.form_box :deep(.el-radio) {
|
|
|
+ margin-right: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 弹窗footer按钮居中 */
|
|
|
+.dialog-footer-center {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.dialog-confirm-btn {
|
|
|
+ padding: 0 32px;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-skeleton-multi {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 700px;
|
|
|
+ background: #f5f6fa;
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+.custom-skeleton-char {
|
|
|
+ position: absolute;
|
|
|
+ color: #e5e6eb;
|
|
|
+ font-weight: bold;
|
|
|
+ letter-spacing: 10px;
|
|
|
+ user-select: none;
|
|
|
+ pointer-events: none;
|
|
|
+ transition: all 0.3s;
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+//页面是否已经加载完毕
|
|
|
+const pageLoading = ref(true)
|
|
|
+const submitLoading = ref(false)
|
|
|
+const showCaptcha = ref(false)
|
|
|
+const captchaImage = ref('')
|
|
|
+const captchaId = ref('')
|
|
|
+const dialogVisible = ref(false)
|
|
|
+
|
|
|
+//1.加载页面必备组件 start---------------------------------------->
|
|
|
+import { ref, onMounted, reactive, nextTick, onBeforeUpdate } from 'vue';
|
|
|
+import { useSeoMeta } from '#imports';
|
|
|
+
|
|
|
+import { ElForm, ElFormItem, ElInput, ElRadioGroup, ElRadio,
|
|
|
+ ElCheckboxGroup, ElCheckbox, ElUpload, ElMessage,
|
|
|
+ ElSelect, ElOption, ElDatePicker, ElButton, genFileId } from 'element-plus'
|
|
|
+
|
|
|
+const { $webUrl, $CwebUrl } = useNuxtApp();
|
|
|
+
|
|
|
+// 表单引用
|
|
|
+const formRef = ref(null)
|
|
|
+
|
|
|
+// 上传组件引用
|
|
|
+const uploadRefs = ref({});
|
|
|
+const fileLists = ref({});
|
|
|
+
|
|
|
+onBeforeUpdate(() => {
|
|
|
+ uploadRefs.value = {};
|
|
|
+});
|
|
|
+
|
|
|
+let adImg = ref({})
|
|
|
+//广告1
|
|
|
+let url = `${$webUrl}/web/getWebsiteAdvertisement?ad_tag=tsbb_index_1`
|
|
|
+const responseAd1 = await fetch(url, {
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ 'Userurl': $CwebUrl,
|
|
|
+ 'Origin': $CwebUrl
|
|
|
+ }
|
|
|
+});
|
|
|
+const resultAd1 = await responseAd1.json();
|
|
|
+adImg.value = resultAd1.data[0];
|
|
|
+
|
|
|
+let adImg_2 = ref({})
|
|
|
+//广告_2
|
|
|
+let url_2 = `${$webUrl}/web/getWebsiteAdvertisement?ad_tag=tsbb_index_2`
|
|
|
+const responseAd_2 = await fetch(url_2, {
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ 'Userurl': $CwebUrl,
|
|
|
+ 'Origin': $CwebUrl
|
|
|
+ }
|
|
|
+});
|
|
|
+const resultAd_2 = await responseAd_2.json();
|
|
|
+adImg_2.value = resultAd_2.data[0];
|
|
|
+
|
|
|
+// 动态验证规则
|
|
|
+const rules_js = ref({})
|
|
|
+
|
|
|
+const get_from_data_1 = reactive({
|
|
|
+ value:{}
|
|
|
+});
|
|
|
+
|
|
|
+const chinese_calendar = reactive({});
|
|
|
+
|
|
|
+//列表头
|
|
|
+const table_head = reactive({
|
|
|
+ value:[]
|
|
|
+});
|
|
|
+
|
|
|
+//列表内容
|
|
|
+const table_body = reactive({
|
|
|
+ value:[]
|
|
|
+});
|
|
|
+
|
|
|
+// 表单数据 - 使用ref而不是reactive来避免响应式问题
|
|
|
+const formData = ref({})
|
|
|
+const route = useRoute()
|
|
|
+
|
|
|
+// 从路由获取 table_id,如果不存在则使用默认值
|
|
|
+const table_id = ref(route.query.table_id || 59)
|
|
|
+
|
|
|
+// 得到id
|
|
|
+const recive_id = reactive({
|
|
|
+ value:route.query.id || route.params.id
|
|
|
+})
|
|
|
+
|
|
|
+// 工具函数定义
|
|
|
+function getFieldName(field) {
|
|
|
+ if (field.field) return field.field;
|
|
|
+ const possibleFieldNames = ['name', 'field_name', 'key', 'id'];
|
|
|
+ for (const name of possibleFieldNames) {
|
|
|
+ if (field[name]) return field[name];
|
|
|
+ }
|
|
|
+ return undefined;
|
|
|
+}
|
|
|
+
|
|
|
+// 检查字段类型的函数,支持数组格式
|
|
|
+function checkFieldType(field, type) {
|
|
|
+ if (!field.field_type) return false;
|
|
|
+ if (Array.isArray(field.field_type)) {
|
|
|
+ return field.field_type.map(Number).includes(Number(type));
|
|
|
+ }
|
|
|
+ return Number(field.field_type) === Number(type);
|
|
|
+}
|
|
|
+
|
|
|
+// 检查是否为数组类型字段(field_type为5或包含5的数组)
|
|
|
+function isArrayTypeField(field) {
|
|
|
+ return checkFieldType(field, 5);
|
|
|
+}
|
|
|
+
|
|
|
+// 生成动态验证规则
|
|
|
+function generateValidationRules() {
|
|
|
+ const rules = {}
|
|
|
+
|
|
|
+ table_head.value.forEach((field, index) => {
|
|
|
+ const fieldRules = []
|
|
|
+ const fieldName = getFieldName(field)
|
|
|
+
|
|
|
+ // 必填验证
|
|
|
+ if (field.is_check === 1) {
|
|
|
+ fieldRules.push({
|
|
|
+ required: true,
|
|
|
+ message: `${field.title}不能为空`,
|
|
|
+ trigger: ['blur', 'change']
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 正则验证
|
|
|
+ if (field.regular && field.regular.trim()) {
|
|
|
+ try {
|
|
|
+ const regex = parseRegExp(field.regular)
|
|
|
+ fieldRules.push({
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
+ if (value && !regex.test(value)) {
|
|
|
+ callback(new Error(`${field.title}格式不正确`))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: ['blur', 'change']
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`正则表达式错误: ${field.regular}`, error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fieldRules.length > 0 && fieldName) {
|
|
|
+ rules[fieldName] = fieldRules
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 添加验证码规则
|
|
|
+ if (showCaptcha.value) {
|
|
|
+ rules['code'] = [
|
|
|
+ { required: true, message: '验证码不能为空', trigger: 'blur' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ rules_js.value = rules
|
|
|
+}
|
|
|
+
|
|
|
+// 刷新验证码
|
|
|
+async function refreshCaptcha() {
|
|
|
+ try {
|
|
|
+ // 拼接 GET 参数
|
|
|
+ const params = new URLSearchParams({ table_id: table_id.value });
|
|
|
+ const url = `${$webUrl}/form/getWebGlobalTableFieldList?${params.toString()}`;
|
|
|
+
|
|
|
+ const response = await fetch(url, {
|
|
|
+ method: 'GET',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ 'Userurl': $CwebUrl,
|
|
|
+ 'Origin': $CwebUrl
|
|
|
+ }
|
|
|
+ });
|
|
|
+ const get_from_data = await response.json();
|
|
|
+
|
|
|
+ // 只处理验证码相关的数据
|
|
|
+ if (get_from_data.data?.table?.is_code === 1 && get_from_data.data?.code?.img) {
|
|
|
+ showCaptcha.value = true
|
|
|
+ captchaId.value = get_from_data.data.code.code_uniqid
|
|
|
+ // 确保base64字符串包含正确的前缀
|
|
|
+ if (get_from_data.data.code.img.startsWith('data:image')) {
|
|
|
+ captchaImage.value = get_from_data.data.code.img
|
|
|
+ } else {
|
|
|
+ captchaImage.value = 'data:image/png;base64,' + get_from_data.data.code.img
|
|
|
+ }
|
|
|
+ // 清空验证码输入框
|
|
|
+ formData.value.code = ''
|
|
|
+ } else {
|
|
|
+ showCaptcha.value = false
|
|
|
+ captchaImage.value = ''
|
|
|
+ captchaId.value = ''
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('刷新验证码失败:', error);
|
|
|
+ ElMessage.error('刷新验证码失败,请重试');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 获取form所有数据
|
|
|
+async function get_from_data_fun() {
|
|
|
+ pageLoading.value = true
|
|
|
+ try {
|
|
|
+ // 拼接 GET 参数
|
|
|
+ const params = new URLSearchParams({ table_id: table_id.value });
|
|
|
+ const url = `${$webUrl}/form/getWebGlobalTableFieldList?${params.toString()}`;
|
|
|
+
|
|
|
+ const response = await fetch(url, {
|
|
|
+ method: 'GET',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ 'Userurl': $CwebUrl,
|
|
|
+ 'Origin': $CwebUrl
|
|
|
+ }
|
|
|
+ });
|
|
|
+ const get_from_data = await response.json();
|
|
|
+
|
|
|
+ // 检查是否需要显示验证码(仅在初始化时)
|
|
|
+ if (get_from_data.data?.table?.is_code === 1 && get_from_data.data?.code?.img) {
|
|
|
+ showCaptcha.value = true
|
|
|
+ captchaId.value = get_from_data.data.code.code_uniqid
|
|
|
+ // 确保base64字符串包含正确的前缀
|
|
|
+ if (get_from_data.data.code.img.startsWith('data:image')) {
|
|
|
+ captchaImage.value = get_from_data.data.code.img
|
|
|
+ } else {
|
|
|
+ captchaImage.value = 'data:image/png;base64,' + get_from_data.data.code.img
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ showCaptcha.value = false
|
|
|
+ captchaImage.value = ''
|
|
|
+ captchaId.value = ''
|
|
|
+ }
|
|
|
+
|
|
|
+ // 赋值表头和表体
|
|
|
+ table_head.value = get_from_data.data?.fields || [];
|
|
|
+ table_body.value = get_from_data.data?.table || [];
|
|
|
+ get_from_data_1.value = get_from_data;
|
|
|
+
|
|
|
+ // 统一 field_type 为数字或数字数组
|
|
|
+ table_head.value.forEach(field => {
|
|
|
+ if (typeof field.field_type === 'string') {
|
|
|
+ if (field.field_type.includes(',')) {
|
|
|
+ field.field_type = field.field_type.split(',').map(Number);
|
|
|
+ } else {
|
|
|
+ field.field_type = Number(field.field_type);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 如果API返回的数据为空,使用默认数据
|
|
|
+ if (table_head.value.length === 0) {
|
|
|
+ table_head.value = [
|
|
|
+ {
|
|
|
+ field: 'name',
|
|
|
+ title: '姓名',
|
|
|
+ field_type: 1,
|
|
|
+ is_check: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'email',
|
|
|
+ title: '邮箱',
|
|
|
+ field_type: 1,
|
|
|
+ is_check: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'hobbies',
|
|
|
+ title: '爱好',
|
|
|
+ field_type: [5],
|
|
|
+ is_check: 0,
|
|
|
+ option_value: {1: "阅读", 2: "运动", 3: "音乐", 4: "旅游"}
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 详细检查每个字段的结构
|
|
|
+ table_head.value.forEach((field, index) => {
|
|
|
+ if (field.option_value) {
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 初始化表单数据
|
|
|
+ table_head.value.forEach(field => {
|
|
|
+ const fieldName = getFieldName(field)
|
|
|
+ if (!fieldName) {
|
|
|
+ console.warn(`字段 ${field.title} 无法确定字段名,跳过初始化`)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据字段类型初始化不同的默认值
|
|
|
+ if (isArrayTypeField(field)) {
|
|
|
+ // 数组类型字段(field_type为5或包含5的数组)初始化为空数组
|
|
|
+ formData.value[fieldName] = []
|
|
|
+ } else if (checkFieldType(field, 7) || checkFieldType(field, 8)) {
|
|
|
+ formData.value[fieldName] = ''
|
|
|
+ fileLists.value[fieldName] = []
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 其他类型初始化为空字符串
|
|
|
+ formData.value[fieldName] = ''
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 单独初始化验证码字段
|
|
|
+ if (showCaptcha.value) {
|
|
|
+ formData.value.code = ''
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成验证规则
|
|
|
+ generateValidationRules()
|
|
|
+ pageLoading.value = false
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('请求失败:', error);
|
|
|
+
|
|
|
+ // 使用默认数据
|
|
|
+ table_head.value = [
|
|
|
+ {
|
|
|
+ field: 'name',
|
|
|
+ title: '姓名',
|
|
|
+ field_type: 1,
|
|
|
+ is_check: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'email',
|
|
|
+ title: '邮箱',
|
|
|
+ field_type: 1,
|
|
|
+ is_check: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ field: 'hobbies',
|
|
|
+ title: '爱好',
|
|
|
+ field_type: [5],
|
|
|
+ is_check: 0,
|
|
|
+ option_value: {1: "阅读", 2: "运动", 3: "音乐", 4: "旅游"}
|
|
|
+ }
|
|
|
+ ]
|
|
|
+
|
|
|
+ // 初始化默认数据
|
|
|
+ table_head.value.forEach(field => {
|
|
|
+ const fieldName = getFieldName(field)
|
|
|
+ if (isArrayTypeField(field)) {
|
|
|
+ formData.value[fieldName] = []
|
|
|
+ } else if (checkFieldType(field, 7) || checkFieldType(field, 8)) {
|
|
|
+ formData.value[fieldName] = ''
|
|
|
+ fileLists.value[fieldName] = []
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ formData.value[fieldName] = ''
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 单独初始化验证码字段
|
|
|
+ if (showCaptcha.value) {
|
|
|
+ formData.value.code = ''
|
|
|
+ }
|
|
|
+
|
|
|
+ generateValidationRules()
|
|
|
+ pageLoading.value = false
|
|
|
+ ElMessage.warning('API请求失败,使用默认数据')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 提交表单
|
|
|
+async function submitForm() {
|
|
|
+ if (!formRef.value) {
|
|
|
+ ElMessage.error('表单引用不存在')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ submitLoading.value = true
|
|
|
+
|
|
|
+ // 手动检查必填字段
|
|
|
+ const requiredFields = table_head.value.filter(field => field.is_check === 1)
|
|
|
+
|
|
|
+ const missingFields = requiredFields.filter(field => {
|
|
|
+ const fieldName = getFieldName(field)
|
|
|
+ if (!fieldName) {
|
|
|
+ console.warn(`字段 ${field.title} 无法确定字段名`)
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ const value = formData.value[fieldName]
|
|
|
+ const isEmpty = value === '' || value === null || value === undefined || (Array.isArray(value) && value.length === 0)
|
|
|
+ return isEmpty
|
|
|
+ })
|
|
|
+
|
|
|
+ if (missingFields.length > 0) {
|
|
|
+ ElMessage.error(`请填写必填字段: ${missingFields.map(f => f.title).join(', ')}`)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 表单验证 - 使用正确的验证方式
|
|
|
+ await formRef.value.validate(async (valid, fields) => {
|
|
|
+ if (valid) {
|
|
|
+ // 构建提交数据
|
|
|
+ const otherDataPayload = { 'table_id': table_id.value };
|
|
|
+ const dataToSubmit = { ...formData.value };
|
|
|
+
|
|
|
+ if (showCaptcha.value) {
|
|
|
+ otherDataPayload.code = dataToSubmit.code;
|
|
|
+ otherDataPayload.code_uniqid = captchaId.value;
|
|
|
+ delete dataToSubmit.code;
|
|
|
+ }
|
|
|
+
|
|
|
+ const submitData = {
|
|
|
+ otherData: otherDataPayload,
|
|
|
+ data: dataToSubmit
|
|
|
+ }
|
|
|
+
|
|
|
+ // 这里可以调用实际的提交接口
|
|
|
+ const response = await fetch(`${$webUrl}/form/addWebGlobalTableData`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ 'Userurl': $CwebUrl,
|
|
|
+ 'Origin': $CwebUrl
|
|
|
+ },
|
|
|
+ body: JSON.stringify(submitData)
|
|
|
+ })
|
|
|
+ const result = await response.json();
|
|
|
+ if(result.code==200){
|
|
|
+ dialogVisible.value = true
|
|
|
+ return
|
|
|
+ }else{
|
|
|
+ ElMessage.error(result.message)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ ElMessage.error('请检查表单填写是否正确')
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('提交失败:', error)
|
|
|
+ ElMessage.error('提交失败,请重试')
|
|
|
+ } finally {
|
|
|
+ submitLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 重置表单
|
|
|
+async function resetForm() {
|
|
|
+ await get_from_data_fun();
|
|
|
+ ElMessage.success('表单已重置');
|
|
|
+}
|
|
|
+
|
|
|
+// 下拉选择对象转数组
|
|
|
+function select_arr_fun(option_obj){
|
|
|
+ if (!option_obj || typeof option_obj !== 'object') return []
|
|
|
+
|
|
|
+ let new_arr = []
|
|
|
+
|
|
|
+ // 遍历对象,key为value,value为label
|
|
|
+ Object.keys(option_obj).forEach(key => {
|
|
|
+ let the_in_obj = {}
|
|
|
+ the_in_obj.label = option_obj[key]
|
|
|
+ the_in_obj.value = +key // 转换为数字
|
|
|
+ new_arr.push(the_in_obj)
|
|
|
+ })
|
|
|
+
|
|
|
+ return new_arr
|
|
|
+}
|
|
|
+
|
|
|
+// 单选按钮对象转数组
|
|
|
+function radio_arr_fun(option_obj) {
|
|
|
+ if (!option_obj || typeof option_obj !== 'object') return []
|
|
|
+
|
|
|
+ let new_arr = []
|
|
|
+
|
|
|
+ // 遍历对象,key为value,value为label
|
|
|
+ Object.keys(option_obj).forEach(key => {
|
|
|
+ new_arr.push([option_obj[key], key]) // [label, value] 格式
|
|
|
+ })
|
|
|
+
|
|
|
+ return new_arr
|
|
|
+}
|
|
|
+
|
|
|
+// 复选框对象转数组
|
|
|
+function checkbox_arr_fun(option_obj) {
|
|
|
+ if (!option_obj || typeof option_obj !== 'object') return []
|
|
|
+
|
|
|
+ let new_arr = []
|
|
|
+
|
|
|
+ // 遍历对象,key为value,value为label
|
|
|
+ Object.keys(option_obj).forEach(key => {
|
|
|
+ let the_in_obj = {}
|
|
|
+ the_in_obj.label = option_obj[key]
|
|
|
+ the_in_obj.value = String(key) // 确保值为字符串类型
|
|
|
+ new_arr.push(the_in_obj)
|
|
|
+ })
|
|
|
+
|
|
|
+ return new_arr
|
|
|
+}
|
|
|
+
|
|
|
+// 文件上传相关
|
|
|
+// 文件超出限制时的处理
|
|
|
+const handleExceed = (files, fieldName) => {
|
|
|
+ const upload = uploadRefs.value[fieldName];
|
|
|
+ if (upload) {
|
|
|
+ upload.clearFiles();
|
|
|
+ const file = files[0];
|
|
|
+ file.uid = genFileId();
|
|
|
+ upload.handleStart(file);
|
|
|
+ upload.submit();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 普通文件上传成功
|
|
|
+const handleFileSuccess = (res, file, fieldName) => {
|
|
|
+ if (res.data && res.data.imgUrl) {
|
|
|
+ formData.value[fieldName] = res.data.imgUrl;
|
|
|
+ if (fileLists.value[fieldName]) {
|
|
|
+ fileLists.value[fieldName] = [{
|
|
|
+ name: file.name,
|
|
|
+ url: res.data.imgUrl
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 文件移除
|
|
|
+const handleFileRemove = (fieldName) => {
|
|
|
+ formData.value[fieldName] = '';
|
|
|
+ if(fileLists.value[fieldName]) {
|
|
|
+ fileLists.value[fieldName] = [];
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// img上传 8
|
|
|
+const handleAvatarSuccess = (res, fieldName) => {
|
|
|
+ if (res.data && res.data.imgUrl) {
|
|
|
+ formData.value[fieldName] = res.data.imgUrl
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const beforeAvatarUpload = (file) => {
|
|
|
+ const isJPG = file.type === 'image/jpeg'
|
|
|
+ const isPNG = file.type === 'image/png'
|
|
|
+ const isLt2M = file.size / 1024 / 1024 < 2
|
|
|
+
|
|
|
+ if (!isJPG && !isPNG) {
|
|
|
+ ElMessage.error('只能上传 JPG/PNG 格式!')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if (!isLt2M) {
|
|
|
+ ElMessage.error('图片大小不能超过 2MB!')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return (isJPG || isPNG) && isLt2M
|
|
|
+}
|
|
|
+
|
|
|
+// 页面加载时获取数据
|
|
|
+onMounted(async () => {
|
|
|
+ await get_from_data_fun()
|
|
|
+
|
|
|
+ // 确保表单引用存在
|
|
|
+ await nextTick()
|
|
|
+
|
|
|
+ if (formRef.value) {
|
|
|
+ } else {
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 复选框变化处理
|
|
|
+function handleCheckboxChange(field, value) {
|
|
|
+ // 这里可以根据需要添加更多的逻辑处理
|
|
|
+}
|
|
|
+
|
|
|
+function parseRegExp(str) {
|
|
|
+ if (typeof str === 'string' && str.startsWith('/') && str.lastIndexOf('/') > 0) {
|
|
|
+ const lastSlash = str.lastIndexOf('/');
|
|
|
+ const pattern = str.slice(1, lastSlash);
|
|
|
+ const flags = str.slice(lastSlash + 1);
|
|
|
+ return new RegExp(pattern, flags);
|
|
|
+ }
|
|
|
+ return new RegExp(str);
|
|
|
+}
|
|
|
+
|
|
|
+function onDialogConfirm() {
|
|
|
+ dialogVisible.value = false;
|
|
|
+ resetForm();
|
|
|
+}
|
|
|
+
|
|
|
+const skeletonChars = Array.from({ length: 16 }).map(() => ({
|
|
|
+ top: Math.random() * 80 + 5, // 5%~85%
|
|
|
+ left: Math.random() * 80 + 5, // 5%~85%
|
|
|
+ fontSize: Math.random() * 60 + 60, // 60~120px
|
|
|
+ opacity: Math.random() * 0.4 + 0.3, // 0.3~0.7
|
|
|
+ rotate: Math.random() * 60 - 30 // -30~30deg
|
|
|
+}));
|
|
|
+</script>
|