panhui 2 éve
szülő
commit
15a6845313

+ 134 - 0
src/components/GenOptionsDialog.vue

@@ -0,0 +1,134 @@
+<template>
+    <el-dialog title="编辑选项" v-model="show" width="500px">
+        <el-table :data="options">
+            <el-table-column label="名称" prop="label">
+                <template #default="{ row }">
+                    <el-input v-model="row.label"></el-input>
+                </template>
+            </el-table-column>
+            <el-table-column label="值" prop="value">
+                <template #default="{ row }">
+                    <el-input v-model="row.value"></el-input>
+                </template>
+            </el-table-column>
+            <el-table-column label="类型">
+                <template #default="{ row }">
+                    <el-select v-model="row.type">
+                        <el-option label="字符串" value="string"></el-option>
+                        <el-option label="数字" value="number"></el-option>
+                        <el-option label="布尔" value="boolean"></el-option>
+                    </el-select>
+                </template>
+            </el-table-column>
+            <el-table-column>
+                <template #default="{ $index }">
+                    <el-button @click="del($index)" type="text">删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <el-button @click="add" type="text" icon="el-icon-plus">添加</el-button>
+        <template #footer>
+            <span class="dialog-footer">
+                <el-button @click="show = false">取 消</el-button>
+                <el-button type="primary" @click="confirm">确 定</el-button>
+            </span>
+        </template>
+    </el-dialog>
+</template>
+
+<script>
+export default {
+    name: 'GenOptionsDialog',
+    props: {
+        visible: {
+            required: true,
+            type: Boolean
+        },
+        value: {}
+    },
+    created() {
+        this.show = this.visible
+        this.update()
+    },
+    data() {
+        return {
+            show: false,
+            options: [
+                {
+                    label: '',
+                    value: '',
+                    type: 'string'
+                }
+            ]
+        }
+    },
+    methods: {
+        update() {
+            if (this.value) {
+                let arr = []
+                JSON.parse(this.value).forEach((item) => {
+                    let value = item.value.toString(),
+                        type = typeof item.value,
+                        label = item.label
+                    arr.push({
+                        label,
+                        value,
+                        type
+                    })
+                })
+                this.options = arr
+            }
+        },
+        confirm() {
+            let arr = []
+            for (let i = 0; i < this.options.length; i++) {
+                if (!this.options[i].label || !this.options[i].value) {
+                    return
+                }
+                let label = this.options[i].label
+                let value
+                switch (this.options[i].type) {
+                    case 'string':
+                        value = this.options[i].value.toString()
+                        break
+                    case 'number':
+                        value = Number(this.options[i].value)
+                        break
+                    case 'boolean':
+                        value = 'false' !== this.options[i].value
+                        break
+                }
+                arr.push({
+                    label: label,
+                    value: value
+                })
+            }
+            this.$emit('input', JSON.stringify(arr))
+            this.$emit('update:visible', false)
+        },
+        add() {
+            this.options.push({
+                label: '',
+                value: '',
+                type: 'string'
+            })
+        },
+        del(index) {
+            this.options.splice(index, 1)
+        }
+    },
+    watch: {
+        visible(val) {
+            this.show = val
+        },
+        show(val) {
+            this.$emit('update:visible', val)
+        },
+        value() {
+            this.update()
+        }
+    }
+}
+</script>
+
+<style scoped></style>

+ 8 - 0
src/router/index.js

@@ -166,6 +166,14 @@ const router = createRouter({
                         title: '知识库管理'
                     }
                 },
+                {
+                    path: 'form',
+                    name: 'form',
+                    component: () => import('../views/FormView.vue'),
+                    meta: {
+                        title: '调查问卷'
+                    }
+                },
                 {
                     path: 'orgUser',
                     name: 'orgUser',

+ 388 - 0
src/views/FormView.vue

@@ -0,0 +1,388 @@
+<template>
+    <PagingTable url="/form/base" :where="where" ref="table">
+        <template #filter>
+            <ElButton :icon="Plus" @click="onEdit()">添加</ElButton>
+            <ElButton :icon="Refresh" @click="table.refresh()"></ElButton>
+        </template>
+        <ElTableColumn prop="id" label="#" width="80" />
+        <ElTableColumn prop="name" label="问卷名称" />
+        <ElTableColumn prop="remark" label="备注" />
+        <ElTableColumn prop="orgId" label="企业ID" />
+        <ElTableColumn prop="used" label="是否启用">
+            <template #default="{ row }">
+                <!-- {{ row.used ? '启用' : '禁用' }} -->
+                <el-switch v-model="row.used" disabled />
+            </template>
+        </ElTableColumn>
+        <ElTableColumn prop="createdAt" label="创建时间" :formatter="timeFormatter" width="150" />
+        <ElTableColumn label="操作" align="center" width="200">
+            <template #default="{ row }">
+                <ElButton @click="onEdit(row)">编辑</ElButton>
+                <ElButton @click="showFiles(row)">表单</ElButton>
+                <ElButton @click="deleteRow(row)" type="danger">删除</ElButton>
+            </template>
+        </ElTableColumn>
+    </PagingTable>
+    <EditDialog v-model="showEditDialog" :model="model" :rules="rules" :on-submit="submit" @success="table.refresh()">
+        <ElFormItem prop="name" label="名称">
+            <ElInput v-model="model.name" placeholder="请输入名称" />
+        </ElFormItem>
+        <ElFormItem prop="remark" label="备注">
+            <ElInput v-model="model.remark" placeholder="请输入备注" />
+        </ElFormItem>
+        <ElFormItem prop="orgId" label="企业ID" v-if="role === 'admin'">
+            <ElInputNumber :controls="false" v-model="model.orgId" placeholder="请输入企业ID" />
+        </ElFormItem>
+        <ElFormItem prop="used" label="启用">
+            <el-switch v-model="model.used" />
+        </ElFormItem>
+    </EditDialog>
+
+    <ElDialog v-model="showFileDialog" :title="selectedBase?.name" width="80%">
+        <PagingTable url="/form/fileds" :where="{ formId: selectedBase?.id }" :order="{ createdAt: 'ASC' }" ref="fileTable">
+            <template #filter>
+                <ElButton :icon="Plus" @click="onFiledEdit()">添加</ElButton>
+                <ElButton :icon="Refresh" @click="fileTable.refresh()"></ElButton>
+            </template>
+            <ElTableColumn prop="id" label="#" width="80" />
+            <ElTableColumn prop="name" label="字段名" />
+            <ElTableColumn prop="remark" label="备注" />
+            <ElTableColumn prop="placeholder" label="输入框占位" />
+            <ElTableColumn prop="formType" label="输入框类型">
+                <template #default="{ row }">
+                    {{ getLabel(row.formType, formTypes) }}
+                </template>
+            </ElTableColumn>
+            <ElTableColumn prop="required" label="是否必填">
+                <template #default="{ row }">
+                    <el-switch v-model="row.required" disabled />
+                </template>
+            </ElTableColumn>
+            <ElTableColumn prop="optionsValue" width="220" show-overflow-tooltip label="选项值" />
+            <ElTableColumn prop="createdAt" label="创建时间" :formatter="timeFormatter" width="150" />
+            <ElTableColumn label="操作" align="center" width="180" fixed="right">
+                <template #default="{ row }">
+                    <ElButton @click="onFiledEdit(row)" type="primary">编辑</ElButton>
+                    <ElButton @click="deleteFiled(row)" type="danger">删除</ElButton>
+                </template>
+            </ElTableColumn>
+        </PagingTable>
+    </ElDialog>
+
+    <EditDialog
+        v-model="showFieldDialog"
+        :model="filedModel"
+        :rules="filedRules"
+        :on-submit="submitFiledModel"
+        @success="fileTable.refresh()"
+        label-width="120px"
+    >
+        <ElFormItem prop="name" label="名称">
+            <ElInput v-model="filedModel.name" placeholder="请输入名称" />
+        </ElFormItem>
+        <ElFormItem prop="remark" label="备注">
+            <ElInput v-model="filedModel.remark" placeholder="请输入备注" />
+        </ElFormItem>
+        <ElFormItem prop="placeholder" label="占位">
+            <ElInput v-model="filedModel.placeholder" placeholder="请输入输入框占位" />
+        </ElFormItem>
+        <ElFormItem prop="required" label="">
+            <el-checkbox v-model="filedModel.required">必填</el-checkbox>
+        </ElFormItem>
+        <ElFormItem prop="formType" label="表单类型">
+            <el-select v-model="filedModel.formType" clearable placeholder="请选择表单类型">
+                <el-option
+                    v-for="item in formTypes"
+                    :label="item.label"
+                    :value="item.value"
+                    :key="item.value"
+                ></el-option>
+            </el-select>
+        </ElFormItem>
+
+        <ElFormItem
+            prop="optionsValue"
+            label="选项值"
+            v-if="
+                filedModel.formType === 'select' ||
+                filedModel.formType === 'multiSelect' ||
+                filedModel.formType === 'radio' ||
+                filedModel.formType === 'checkbox'
+            "
+        >
+            <ElInput v-model="filedModel.optionsValue" placeholder="选项值">
+                <template #append>
+                    <el-button type="text" @click="editOptions(filedModel)">编辑 </el-button>
+                </template>
+            </ElInput>
+        </ElFormItem>
+        <ElFormItem prop="validate" label="">
+            <el-checkbox v-model="filedModel.validate">验证</el-checkbox>
+        </ElFormItem>
+        <template v-if="filedModel.validate">
+            <ElFormItem prop="validatorType" label="验证类型">
+                <el-select v-model="filedModel.validatorType" clearable placeholder="请选择验证类型">
+                <el-option
+                    v-for="item in validatorTypes"
+                    :label="item.label"
+                    :value="item.value"
+                    :key="item.value"
+                ></el-option>
+            </el-select>
+            </ElFormItem>
+            <ElFormItem prop="minLength" label="最短长度">
+                <ElInput v-model="filedModel.minLength" placeholder="请输入最短长度" />
+            </ElFormItem>
+            <ElFormItem prop="maxLength" label="最大长度">
+                <ElInput v-model="filedModel.maxLength" placeholder="请输入最大长度" />
+            </ElFormItem>
+            <ElFormItem prop="min" label="最小值">
+                <ElInput v-model="filedModel.min" placeholder="请输入最小值" />
+            </ElFormItem>
+            <ElFormItem prop="max" label="最大值">
+                <ElInput v-model="filedModel.max" placeholder="请输入最大值" />
+            </ElFormItem>
+        </template>
+        <!-- <ElTableColumn prop="formType" label="表单类型" />
+            <ElTableColumn prop="required" label="必填" />
+            <ElTableColumn prop="validate" label="表单验证" />
+            <ElTableColumn prop="minLength" label="最短长度" />
+            <ElTableColumn prop="maxLength" label="最大长度" />
+            <ElTableColumn prop="min" label="最小值" />
+            <ElTableColumn prop="max" label="最大值" />
+            <ElTableColumn prop="validatorType" label="验证类型" /> -->
+    </EditDialog>
+
+    <el-dialog title="编辑选项" v-model="showGenOptionsDialog" width="500px">
+        <el-table :data="options">
+            <el-table-column label="名称" prop="label">
+                <template #default="{ row }">
+                    <el-input v-model="row.label"></el-input>
+                </template>
+            </el-table-column>
+            <el-table-column label="值" prop="value">
+                <template #default="{ row }">
+                    <el-input v-model="row.value"></el-input>
+                </template>
+            </el-table-column>
+            <el-table-column label="类型">
+                <template #default="{ row }">
+                    <el-select v-model="row.type">
+                        <el-option label="字符串" value="string"></el-option>
+                        <el-option label="数字" value="number"></el-option>
+                        <el-option label="布尔" value="boolean"></el-option>
+                    </el-select>
+                </template>
+            </el-table-column>
+            <el-table-column>
+                <template #default="{ $index }">
+                    <el-button @click="delOptions($index)" type="text">删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <el-button @click="addOptions" type="text" icon="el-icon-plus">添加</el-button>
+        <template #footer>
+            <span class="dialog-footer">
+                <el-button @click="showGenOptionsDialog = false">取 消</el-button>
+                <el-button type="primary" @click="confirmOptions">确 定</el-button>
+            </span>
+        </template>
+    </el-dialog>
+</template>
+<script setup>
+import { ref, computed, nextTick } from 'vue'
+import PagingTable from '@/components/PagingTable.vue'
+import { useEnumFormatter, useTimeFormatter } from '@/utils/formatter'
+import { Plus, Refresh } from '@vicons/tabler'
+import EditDialog from '@/components/EditDialog.vue'
+import { setupEditDialog } from '@/utils/editDialog'
+import { http } from '@/plugins/http'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { useClipboard } from '@vueuse/core'
+import { storeToRefs } from 'pinia'
+import { useUserStore } from '@/stores/user'
+import { FileStatus } from '@/enums'
+import { useFileDialog } from '@vueuse/core'
+
+const { user } = storeToRefs(useUserStore())
+const role = computed(() => user.value?.roles[0])
+const where = computed(() => {
+    if (role.value === 'admin') return {}
+    return {
+        orgId: user.value.orgId
+    }
+})
+const timeFormatter = useTimeFormatter()
+const table = ref(null)
+const model = ref({
+    used: false
+})
+const rules = {
+    name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
+    // remark: [{ required: true, message: '请输入备注', trigger: 'blur' }],
+    orgId: [{ required: true, message: '请输入企业ID', trigger: 'blur' }]
+}
+const { showEditDialog, onEdit } = setupEditDialog(model)
+async function submit() {
+    if (!model.value.orgId) {
+        model.value.orgId = user.value.orgId
+    }
+    await http.put(model.value.id ? `/form/base/${model.value.id}` : '/form/base', model.value)
+    ElMessage.success('保存成功')
+}
+async function deleteRow(row) {
+    try {
+        await ElMessageBox.confirm('此操作将永久删除数据, 是否继续?', '提示', {
+            type: 'warning'
+        })
+        await http.delete(`/form/base/${row.id}`)
+        ElMessage.success('删除成功')
+        table.value.refresh()
+    } catch (error) {
+        if ('cancel' !== error) ElMessage.error(error.message)
+    }
+}
+const selectedBase = ref(null)
+const fileTable = ref(null)
+function showFiles(row) {
+    selectedBase.value = row
+    showFileDialog.value = true
+    nextTick(() => {
+        fileTable.value.refresh()
+    })
+}
+const showFileDialog = ref(false)
+
+const fieldTypes = ['char', 'varchar', 'int', 'bit', 'text', 'timestamp', 'datetime', 'decimal', 'float', 'vue']
+const searchMethods = ['=', '!=', '>', '>=', '<', '<=', 'between', 'like', 'left like', 'right like']
+const formTypes = [
+    { label: '单行文本', value: 'singleLineText' },
+    { label: '数字', value: 'number' },
+    { label: '日期选择', value: 'date' },
+    { label: '日期时间选择', value: 'datetime' },
+    { label: '登录名', value: 'loginName' },
+    { label: '当前时间', value: 'currentTime' },
+    { label: '当前日期', value: 'currentDate' },
+    { label: '单选框', value: 'radio' },
+    { label: '单选下拉框', value: 'select' },
+    { label: '多选框', value: 'checkbox' },
+    { label: '多选下拉框', value: 'multiSelect' },
+    { label: '单图上传', value: 'singleImage' },
+    { label: '多图上传', value: 'multiImage' },
+    { label: '文件上传', value: 'fileUpload' },
+    { label: '树形选择', value: 'tree' },
+    { label: '开关', value: 'switch' },
+    { label: '多行文本', value: 'textarea' },
+    { label: '富文本', value: 'richText' }
+]
+const validatorTypes = [
+    { label: '英文', value: 'english' },
+    { label: '数字', value: 'number' },
+    { label: '手机', value: 'phone' },
+    { label: '网址', value: 'url' },
+    { label: '电子邮件', value: 'email' },
+    { label: '身份证', value: 'id' }
+]
+
+function getLabel(val = '', list = []) {
+    console.log(val)
+    console.log(list)
+    let info = list.find((item) => {
+        return item.value === val
+    })
+    console.log(info)
+    return info ? info.label : ''
+}
+const filedRules = {
+    name: [
+        { required: true, message: '请填写表名', trigger: 'blur' },
+        {
+            validator: (rule, value, callback) => {
+                if (value) {
+                    if (/^[\u4e00-\u9fa5a-zA-Z0-9_]*$/.test(value)) {
+                        callback()
+                    } else {
+                        callback(new Error('表名不正确'))
+                    }
+                }
+            },
+            trigger: 'blur'
+        }
+    ],
+    formType: [
+        {
+            required: true,
+            message: '请选择表单类型',
+            trigger: 'blur'
+        }
+    ]
+}
+
+const filedModel = ref({
+    apiFlag: 'singleLineText',
+    optionsValue: ''
+})
+const { showEditDialog: showFieldDialog, onEdit: onFiledEdit } = setupEditDialog(filedModel)
+async function submitFiledModel() {
+    if (!filedModel.value.formId) {
+        filedModel.value.formId = selectedBase.value.id
+    }
+    await http.put(
+        filedModel.value.id ? `/form/updateFiled/${filedModel.value.id}` : '/form/createFiled',
+        filedModel.value
+    )
+    ElMessage.success('保存成功')
+}
+
+const tempRow = ref({})
+
+const { showEditDialog: showGenOptionsDialog, onEdit: editOptions } = setupEditDialog(tempRow)
+const options = ref([
+    {
+        label: '',
+        value: '',
+        type: 'string'
+    }
+])
+
+function addOptions() {
+    options.value.push({
+        label: '',
+        value: '',
+        type: 'string'
+    })
+}
+
+function confirmOptions() {
+    let arr = []
+    for (let i = 0; i < options.value.length; i++) {
+        if (!options.value[i].label || !options.value[i].value) {
+            return
+        }
+        let label = options.value[i].label
+        let value
+        switch (options.value[i].type) {
+            case 'string':
+                value = options.value[i].value.toString()
+                break
+            case 'number':
+                value = Number(options.value[i].value)
+                break
+            case 'boolean':
+                value = 'false' !== options.value[i].value
+                break
+        }
+        arr.push({
+            label: label,
+            value: value
+        })
+    }
+
+    filedModel.value.optionsValue = JSON.stringify(arr)
+    showGenOptionsDialog.value = false
+}
+
+function delOptions(index) {
+    options.value.splice(index, 1)
+}
+</script>

+ 14 - 17
src/views/HomeView.vue

@@ -112,23 +112,20 @@ watch(activeName, () => {
 })
 
 async function initChart() {
-    if (activeName.value === 'user') {
-        await http
-            .get('/admin/users/getDatas', {
-                apiUserId: user.value.orgId
-            })
-            .then((res) => {
-                userDatas.value = res
-            })
-    } else {
-        await http
-            .get('/chat/getDatas', {
-                apiUserId: user.value.orgId
-            })
-            .then((res) => {
-                chatDatas.value = res
-            })
-    }
+    await http
+        .get('/admin/users/getDatas', {
+            apiUserId: user.value.orgId
+        })
+        .then((res) => {
+            userDatas.value = res
+        })
+    await http
+        .get('/chat/getDatas', {
+            apiUserId: user.value.orgId
+        })
+        .then((res) => {
+            chatDatas.value = res
+        })
 
     if (myChart.value) {
         myChart.value.dispose()

+ 9 - 0
src/views/MainView.vue

@@ -115,6 +115,10 @@ const menus = computed(() => {
                     {
                         name: 'knowledge',
                         title: '知识库管理'
+                    },
+                    {
+                        name: 'form',
+                        title: '调查问卷'
                     }
                 ]
             },
@@ -199,6 +203,11 @@ const menus = computed(() => {
                 title: '知识库管理',
                 icon: Notebook
             },
+            // {
+            //     name: 'form',
+            //     title: '调查问卷',
+            //     icon: Notebook
+            // },
             {
                 name: 'orgUser',
                 title: '席位管理',