feat: 前端新增白名单功能
This commit is contained in:
parent
0b53b78cda
commit
df6e14c585
2
web/.gitignore
vendored
2
web/.gitignore
vendored
@ -26,4 +26,4 @@ yarn.lock
|
|||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
.history
|
.history
|
||||||
|
1
web/components.d.ts
vendored
1
web/components.d.ts
vendored
@ -41,6 +41,7 @@ declare module 'vue' {
|
|||||||
ElTag: (typeof import('element-plus/es'))['ElTag']
|
ElTag: (typeof import('element-plus/es'))['ElTag']
|
||||||
ElTimePicker: (typeof import('element-plus/es'))['ElTimePicker']
|
ElTimePicker: (typeof import('element-plus/es'))['ElTimePicker']
|
||||||
ElTooltip: (typeof import('element-plus/es'))['ElTooltip']
|
ElTooltip: (typeof import('element-plus/es'))['ElTooltip']
|
||||||
|
ElTree: (typeof import('element-plus/es'))['ElTree']
|
||||||
IEpBottom: (typeof import('~icons/ep/bottom'))['default']
|
IEpBottom: (typeof import('~icons/ep/bottom'))['default']
|
||||||
IEpCheck: (typeof import('~icons/ep/check'))['default']
|
IEpCheck: (typeof import('~icons/ep/check'))['default']
|
||||||
IEpCirclePlus: (typeof import('~icons/ep/circle-plus'))['default']
|
IEpCirclePlus: (typeof import('~icons/ep/circle-plus'))['default']
|
||||||
|
11
web/src/common/regexpMap.ts
Normal file
11
web/src/common/regexpMap.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const regexpMap = {
|
||||||
|
nd: /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/,
|
||||||
|
m: /^[1]([3-9])[0-9]{9}$/,
|
||||||
|
idcard: /^(\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$/,
|
||||||
|
strictIdcard:
|
||||||
|
/(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$)/,
|
||||||
|
n: /^[0-9]+([.]{1}[0-9]+){0,1}$/,
|
||||||
|
e: /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/,
|
||||||
|
licensePlate:
|
||||||
|
/^([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[a-zA-Z](([DFAG]((?![IO])[a-zA-Z0-9](?![IO]))[0-9]{4})|([0-9]{5}[DF]))|[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4,5}[A-Z0-9挂学警港澳]{1})$/
|
||||||
|
}
|
@ -18,6 +18,10 @@ export const getSpaceDetail = (workspaceId: string) => {
|
|||||||
return axios.get(`/workspace/${workspaceId}`)
|
return axios.get(`/workspace/${workspaceId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getMemberList = () => {
|
||||||
|
return axios.get('/workspace/member/list')
|
||||||
|
}
|
||||||
|
|
||||||
export const deleteSpace = (workspaceId: string) => {
|
export const deleteSpace = (workspaceId: string) => {
|
||||||
return axios.delete(`/workspace/${workspaceId}`)
|
return axios.delete(`/workspace/${workspaceId}`)
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="nav" v-if="slots.hasOwnProperty('nav')">
|
<div class="nav" v-if="slots.nav">
|
||||||
<slot name="nav"></slot>
|
<slot name="nav"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<slot v-if="slots.hasOwnProperty('body')" name="body"></slot>
|
<slot v-if="slots.body" name="body"></slot>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="left" v-if="slots.hasOwnProperty('left')">
|
<div class="left" v-if="slots.left">
|
||||||
<slot name="left"></slot>
|
<slot name="left"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="center" v-if="slots.hasOwnProperty('center')">
|
<div class="center" v-if="slots.center">
|
||||||
<slot name="center"></slot>
|
<slot name="center"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="right" v-if="slots.hasOwnProperty('right')">
|
<div class="right" v-if="slots.right">
|
||||||
<slot name="right"></slot>
|
<slot name="right"></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -113,7 +113,7 @@ const normalizationValues = (configList: Array<any> = []) => {
|
|||||||
if (item.hidden) {
|
if (item.hidden) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 动态显隐设置器
|
// 动态显隐设置器
|
||||||
if (_isFunction(item.relyFunc)) {
|
if (_isFunction(item.relyFunc)) {
|
||||||
return item.relyFunc(props.moduleConfig)
|
return item.relyFunc(props.moduleConfig)
|
||||||
|
@ -32,6 +32,17 @@ const updateLogicConf = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateWhiteConf = () => {
|
||||||
|
const baseConf = store.state.edit.schema.baseConf || {};
|
||||||
|
if (baseConf.passwordSwitch && !baseConf.password) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (baseConf.whitelistType!='ALL' && !baseConf.whitelist?.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const handlePublish = async () => {
|
const handlePublish = async () => {
|
||||||
if (isPublishing.value) {
|
if (isPublishing.value) {
|
||||||
return
|
return
|
||||||
@ -54,6 +65,12 @@ const handlePublish = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(updateWhiteConf()){
|
||||||
|
isPublishing.value = false
|
||||||
|
ElMessage.error('请检查问卷设置是否有误')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const saveRes: any = await saveSurvey(saveData)
|
const saveRes: any = await saveSurvey(saveData)
|
||||||
if (saveRes.code !== 200) {
|
if (saveRes.code !== 200) {
|
||||||
|
@ -65,6 +65,17 @@ const updateLogicConf = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateWhiteConf = () => {
|
||||||
|
const baseConf = store.state.edit.schema.baseConf || {};
|
||||||
|
if (baseConf.passwordSwitch && !baseConf.password) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (baseConf.whitelistType!='ALL' && !baseConf.whitelist?.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const timerHandle = ref<NodeJS.Timeout | number | null>(null)
|
const timerHandle = ref<NodeJS.Timeout | number | null>(null)
|
||||||
const triggerAutoSave = () => {
|
const triggerAutoSave = () => {
|
||||||
if (autoSaveStatus.value === 'saving') {
|
if (autoSaveStatus.value === 'saving') {
|
||||||
@ -116,6 +127,12 @@ const handleSave = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(updateWhiteConf()){
|
||||||
|
isSaving.value = false
|
||||||
|
ElMessage.error('请检查问卷设置是否有误')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res: any = await saveData()
|
const res: any = await saveData()
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
|
@ -39,7 +39,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, onMounted, shallowRef } from 'vue'
|
import { computed, ref, onMounted, shallowRef } from 'vue'
|
||||||
import { useEditStore } from '@/management/stores/edit'
|
import { useEditStore } from '@/management/stores/edit'
|
||||||
import { cloneDeep as _cloneDeep, isArray as _isArray, get as _get } from 'lodash-es'
|
import { useStore } from 'vuex'
|
||||||
|
import {
|
||||||
|
cloneDeep as _cloneDeep,
|
||||||
|
isArray as _isArray,
|
||||||
|
get as _get,
|
||||||
|
isFunction as _isFunction
|
||||||
|
} from 'lodash-es'
|
||||||
|
|
||||||
import baseConfig from './config/baseConfig'
|
import baseConfig from './config/baseConfig'
|
||||||
import baseFormConfig from './config/baseFormConfig'
|
import baseFormConfig from './config/baseFormConfig'
|
||||||
@ -51,6 +57,8 @@ const components = shallowRef<any>({})
|
|||||||
const registerTypes = ref<any>({})
|
const registerTypes = ref<any>({})
|
||||||
const editStore = useEditStore()
|
const editStore = useEditStore()
|
||||||
const { schema, changeSchema } = editStore
|
const { schema, changeSchema } = editStore
|
||||||
|
const store = useStore()
|
||||||
|
const schemaBaseConf = computed(() => store.state.edit?.schema?.baseConf || {})
|
||||||
|
|
||||||
const setterList = computed(() => {
|
const setterList = computed(() => {
|
||||||
const list = _cloneDeep(formConfigList.value)
|
const list = _cloneDeep(formConfigList.value)
|
||||||
@ -74,6 +82,13 @@ const setterList = computed(() => {
|
|||||||
}
|
}
|
||||||
formItem.value = formValue
|
formItem.value = formValue
|
||||||
}
|
}
|
||||||
|
// 动态显隐设置器
|
||||||
|
form.formList = form.formList.filter((item: any) => {
|
||||||
|
if (_isFunction(item.relyFunc)) {
|
||||||
|
return item.relyFunc(schemaBaseConf.value)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
form.dataConfig = dataConfig
|
form.dataConfig = dataConfig
|
||||||
|
|
||||||
|
@ -8,5 +8,10 @@ export default [
|
|||||||
title: '提交限制',
|
title: '提交限制',
|
||||||
key: 'limitConfig',
|
key: 'limitConfig',
|
||||||
formList: ['limit_tLimit']
|
formList: ['limit_tLimit']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '作答限制',
|
||||||
|
key: 'respondConfig',
|
||||||
|
formList: ['interview_pwd','answer_type','white_placeholder','white_list','team_list']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -21,5 +21,44 @@ export default {
|
|||||||
tip: '问卷仅在指定时间段内可填写',
|
tip: '问卷仅在指定时间段内可填写',
|
||||||
type: 'QuestionTimeHour',
|
type: 'QuestionTimeHour',
|
||||||
placement: 'top'
|
placement: 'top'
|
||||||
|
},
|
||||||
|
interview_pwd: {
|
||||||
|
keys: ['baseConf.passwordSwitch', 'baseConf.password'],
|
||||||
|
label: '访问密码',
|
||||||
|
type: 'SwitchInput',
|
||||||
|
placeholder: '请输入6位字符串类型访问密码 ',
|
||||||
|
maxLength: 6,
|
||||||
|
},
|
||||||
|
answer_type: {
|
||||||
|
key: 'baseConf.whitelistType',
|
||||||
|
label: '答题名单',
|
||||||
|
type: 'AnswerRadio',
|
||||||
|
},
|
||||||
|
white_placeholder:{
|
||||||
|
key: 'baseConf.whitelistTip',
|
||||||
|
label: '名单登录提示语',
|
||||||
|
placeholder:'请输入名单提示语',
|
||||||
|
type: 'InputWordLimit',
|
||||||
|
maxLength: 40,
|
||||||
|
relyFunc: (data) => {
|
||||||
|
return ['CUSTOM','MEMBER'].includes(data.whitelistType)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
white_list:{
|
||||||
|
keys: ['baseConf.whitelist','baseConf.memberType'],
|
||||||
|
label: '白名单列表',
|
||||||
|
type: 'whiteList',
|
||||||
|
relyFunc: (data) => {
|
||||||
|
|
||||||
|
return data.whitelistType == 'CUSTOM'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
team_list:{
|
||||||
|
key: 'baseConf.whitelist',
|
||||||
|
label: '团队空间成员选择',
|
||||||
|
type: 'teamMemberList',
|
||||||
|
relyFunc: (data) => {
|
||||||
|
return data.whitelistType == 'MEMBER'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
web/src/materials/setters/widgets/AnswerRadio.vue
Normal file
41
web/src/materials/setters/widgets/AnswerRadio.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<div class="answer-radio-wrap">
|
||||||
|
<el-radio-group v-model="whitelistType" @change="handleRadioGroupChange">
|
||||||
|
<el-radio value="ALL">所有人</el-radio>
|
||||||
|
<el-radio value="MEMBER">空间成员</el-radio>
|
||||||
|
<el-radio value="CUSTOM">白名单</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
formConfig: Object,
|
||||||
|
})
|
||||||
|
const emit = defineEmits([FORM_CHANGE_EVENT_KEY])
|
||||||
|
|
||||||
|
const whitelistType = ref(props.formConfig?.value || 'ALL')
|
||||||
|
|
||||||
|
const handleRadioGroupChange = (value) => {
|
||||||
|
const key = props.formConfig.key
|
||||||
|
emit(FORM_CHANGE_EVENT_KEY, { key, value })
|
||||||
|
emit(FORM_CHANGE_EVENT_KEY, { key:'baseConf.whitelist', value: [] })
|
||||||
|
emit(FORM_CHANGE_EVENT_KEY, { key: 'baseConf.memberType', value: 'MOBILE' })
|
||||||
|
if (whitelistType.value == 'ALL') {
|
||||||
|
emit(FORM_CHANGE_EVENT_KEY, { key:'baseConf.whitelistTip', value:'' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.switch-input-wrap{
|
||||||
|
width: 100%;
|
||||||
|
.mt16{
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
33
web/src/materials/setters/widgets/InputWordLimit.vue
Normal file
33
web/src/materials/setters/widgets/InputWordLimit.vue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<el-input
|
||||||
|
:maxlength="maxLength"
|
||||||
|
v-model="modelValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
show-word-limit
|
||||||
|
type="text"
|
||||||
|
@change="handleInputChange"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { computed,ref } from 'vue'
|
||||||
|
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
formConfig: Object,
|
||||||
|
})
|
||||||
|
const emit = defineEmits([FORM_CHANGE_EVENT_KEY])
|
||||||
|
|
||||||
|
const modelValue = ref(props.formConfig.value || '')
|
||||||
|
|
||||||
|
const maxLength = computed(() => props.formConfig.maxLength || 10)
|
||||||
|
|
||||||
|
const placeholder = computed(() => props.formConfig.placeholder || '')
|
||||||
|
|
||||||
|
const handleInputChange = (value) => {
|
||||||
|
const key = props.formConfig.key
|
||||||
|
|
||||||
|
modelValue.value = value
|
||||||
|
emit(FORM_CHANGE_EVENT_KEY, { key, value })
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
52
web/src/materials/setters/widgets/SwitchInput.vue
Normal file
52
web/src/materials/setters/widgets/SwitchInput.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div class="switch-input-wrap">
|
||||||
|
<el-switch v-model="passwordSwitch" @change="changeData(props.formConfig.keys[0],passwordSwitch)" />
|
||||||
|
<InputWordLimit
|
||||||
|
v-if="passwordSwitch"
|
||||||
|
class="mt16"
|
||||||
|
@form-change="handleFormChange"
|
||||||
|
:formConfig="{
|
||||||
|
...props.formConfig,
|
||||||
|
key: props.formConfig.keys[1],
|
||||||
|
value:props.formConfig?.value[1]
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
|
||||||
|
import InputWordLimit from './InputWordLimit.vue'
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
const props = defineProps({
|
||||||
|
formConfig: Object,
|
||||||
|
})
|
||||||
|
const emit = defineEmits([FORM_CHANGE_EVENT_KEY])
|
||||||
|
const passwordSwitch = ref(props.formConfig?.value[0] || false);
|
||||||
|
|
||||||
|
|
||||||
|
const changeData = (key, value) => {
|
||||||
|
emit(FORM_CHANGE_EVENT_KEY, {
|
||||||
|
key,
|
||||||
|
value
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
const handleFormChange = (data) => {
|
||||||
|
store.dispatch('edit/changeSchema', {
|
||||||
|
key: data.key,
|
||||||
|
value: data.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.switch-input-wrap{
|
||||||
|
width: 100%;
|
||||||
|
.mt16{
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
96
web/src/materials/setters/widgets/teamMemberList.vue
Normal file
96
web/src/materials/setters/widgets/teamMemberList.vue
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<div class="team-member-wrap">
|
||||||
|
<div class="team-tree-wrap">
|
||||||
|
<el-tree ref="treeRef" :default-expanded-keys="defaultCheckedKeys" :default-checked-keys="defaultCheckedKeys"
|
||||||
|
:data="treeData" empty-text="暂无数据" @check="handleChange" style="height:201px" highlight-current show-checkbox
|
||||||
|
node-key="id" :props="defaultProps" />
|
||||||
|
</div>
|
||||||
|
<div class="member-count">已选择 <span>{{ selectCount }}</span> 人</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, defineProps, defineEmits, onMounted } from 'vue'
|
||||||
|
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
|
||||||
|
import {
|
||||||
|
getMemberList
|
||||||
|
} from '@/management/api/space'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
formConfig: Object,
|
||||||
|
})
|
||||||
|
const emit = defineEmits([FORM_CHANGE_EVENT_KEY])
|
||||||
|
|
||||||
|
const treeRef = ref(null)
|
||||||
|
const treeData = ref([])
|
||||||
|
const defaultCheckedKeys = ref([])
|
||||||
|
const defaultProps = {
|
||||||
|
children: 'children',
|
||||||
|
label: 'label',
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = () => {
|
||||||
|
const key = props.formConfig.key;
|
||||||
|
const userKeys = treeRef.value?.getCheckedKeys(true);
|
||||||
|
if (userKeys.length > 100) {
|
||||||
|
ElMessage.error('最多添加100个')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit(FORM_CHANGE_EVENT_KEY, { key: key, value: userKeys });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const selectCount = computed(() => {
|
||||||
|
return treeRef.value?.getCheckedKeys(true).length || 0
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const getSpaceMenus = async () => {
|
||||||
|
const res = await getMemberList();
|
||||||
|
if (res.code != 200) {
|
||||||
|
ElMessage.error('获取空间成员列表失败');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const data = res.data;
|
||||||
|
data.map((v) => {
|
||||||
|
const members = v.members || [];
|
||||||
|
treeData.value.push({
|
||||||
|
id: v.ownerId,
|
||||||
|
label: v.name,
|
||||||
|
children: members?.map(v => ({
|
||||||
|
id: v.userId,
|
||||||
|
label: v.role,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
defaultCheckedKeys.value = props.formConfig.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getSpaceMenus();
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.team-member-wrap {
|
||||||
|
width: 508px;
|
||||||
|
|
||||||
|
.team-tree-wrap {
|
||||||
|
background: #FFFFFF;
|
||||||
|
border: 1px solid rgba(227, 228, 232, 1);
|
||||||
|
border-radius: 2px;
|
||||||
|
min-height: 204px;
|
||||||
|
max-height: 204px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-count {
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: $primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
145
web/src/materials/setters/widgets/whiteList.vue
Normal file
145
web/src/materials/setters/widgets/whiteList.vue
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<div class="white-list-wrap">
|
||||||
|
<el-button class="create-btn" type="primary" @click="whiteVisible=true">
|
||||||
|
添加
|
||||||
|
</el-button>
|
||||||
|
<el-button v-if="whitelist.length>0" class="create-btn" color="#4A4C5B" @click="delAllList">
|
||||||
|
全部删除
|
||||||
|
</el-button>
|
||||||
|
<el-table class="table-wrap" empty-text="暂无数据" :data="whitelist" height="240" style="width: 426px">
|
||||||
|
<el-table-column label="名单" width="350" >
|
||||||
|
<template #default="scope">
|
||||||
|
<div>{{ whitelist[scope.$index] }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="74" >
|
||||||
|
<template #default="scope">
|
||||||
|
<div @click="delRowItem(scope.$index)" class="flex cursor"><i-ep-delete :size="16" /></div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-dialog v-model="whiteVisible" title="添加白名单" width="600" @closed="handleClose">
|
||||||
|
<div>
|
||||||
|
<el-form-item label-position="top" label="类型选择" label-width="auto">
|
||||||
|
<el-radio-group v-model="memberType" >
|
||||||
|
<el-radio value="MOBILE">手机号</el-radio>
|
||||||
|
<el-radio value="EMAIL">邮箱</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label-position="top" class="flex-column" label="名单录入" label-width="auto">
|
||||||
|
<el-input v-model="whiteTextarea" placeholder="多个用逗号(半角)“,”隔开" rows="7" resize="none" type="textarea" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="whiteVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleChange">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { ref,nextTick } from 'vue'
|
||||||
|
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { regexpMap } from '@/common/regexpMap.ts'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
formConfig: Object,
|
||||||
|
})
|
||||||
|
const emit = defineEmits([FORM_CHANGE_EVENT_KEY])
|
||||||
|
|
||||||
|
const whitelist = ref(props.formConfig.value[0] || [])
|
||||||
|
const memberType = ref(props.formConfig.value[1] || 'MOBILE')
|
||||||
|
const whiteVisible = ref(false)
|
||||||
|
const whiteTextarea = ref(whitelist.value.join(','))
|
||||||
|
|
||||||
|
const regularMap = {
|
||||||
|
MOBILE:regexpMap.m,
|
||||||
|
EMAIL:regexpMap.e
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const checkValRule = (list) => {
|
||||||
|
let status = false;
|
||||||
|
if (list.length > 100) {
|
||||||
|
ElMessage.error('最多添加100个')
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
const pattern = regularMap[memberType.value];
|
||||||
|
if(!pattern) return false;
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (!pattern.test(list[i])) {
|
||||||
|
status = true;
|
||||||
|
ElMessage.error('数据格式错误,请检查后重新输入~')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleChange = () => {
|
||||||
|
const keys = props.formConfig.keys;
|
||||||
|
const list = whiteTextarea.value ? whiteTextarea.value.split(',') : []
|
||||||
|
if(checkValRule(list)) return
|
||||||
|
emit(FORM_CHANGE_EVENT_KEY, { key:keys[0], value: list });
|
||||||
|
emit(FORM_CHANGE_EVENT_KEY, { key: keys[1], value: memberType.value })
|
||||||
|
whiteVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
whitelist.value = props.formConfig.value[0] || []
|
||||||
|
whiteTextarea.value = whitelist.value.join(',')
|
||||||
|
memberType.value = props.formConfig.value[1] || 'MOBILE'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const delRowItem = (index) => {
|
||||||
|
whitelist.value.splice(index, 1);
|
||||||
|
whiteTextarea.value = whitelist.value.join(',')
|
||||||
|
const keys = props.formConfig.keys;
|
||||||
|
emit(FORM_CHANGE_EVENT_KEY, { key:keys[0], value: whitelist.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
const delAllList = () => {
|
||||||
|
whitelist.value = []
|
||||||
|
whiteTextarea.value = ''
|
||||||
|
handleChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.white-list-wrap {
|
||||||
|
.flex-column{
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
:deep(th){
|
||||||
|
padding:4px 0;
|
||||||
|
background: #F6F7F9;
|
||||||
|
}
|
||||||
|
:deep(td){
|
||||||
|
padding:6px 0;
|
||||||
|
}
|
||||||
|
.table-wrap{
|
||||||
|
margin-top: 16px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.cursor{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.flex{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -6,18 +6,7 @@ import {
|
|||||||
set as _set
|
set as _set
|
||||||
} from 'lodash-es'
|
} from 'lodash-es'
|
||||||
import { INPUT, RATES, QUESTION_TYPE } from '@/common/typeEnum.ts'
|
import { INPUT, RATES, QUESTION_TYPE } from '@/common/typeEnum.ts'
|
||||||
|
import { regexpMap } from '@/common/regexpMap.ts'
|
||||||
const regexpMap = {
|
|
||||||
nd: /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/,
|
|
||||||
m: /^[1]([3-9])[0-9]{9}$/,
|
|
||||||
idcard: /^(\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$/,
|
|
||||||
strictIdcard:
|
|
||||||
/(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$)/,
|
|
||||||
n: /^[0-9]+([.]{1}[0-9]+){0,1}$/,
|
|
||||||
e: /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/,
|
|
||||||
licensePlate:
|
|
||||||
/^([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[a-zA-Z](([DFAG]((?![IO])[a-zA-Z0-9](?![IO]))[0-9]{4})|([0-9]{5}[DF]))|[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4,5}[A-Z0-9挂学警港澳]{1})$/
|
|
||||||
}
|
|
||||||
|
|
||||||
const msgMap = {
|
const msgMap = {
|
||||||
'*': '必填',
|
'*': '必填',
|
||||||
|
@ -32,3 +32,10 @@ export const queryVote = ({ surveyPath, fieldList }) => {
|
|||||||
export const getEncryptInfo = () => {
|
export const getEncryptInfo = () => {
|
||||||
return axios.get('/clientEncrypt/getEncryptInfo')
|
return axios.get('/clientEncrypt/getEncryptInfo')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const validate = ({ surveyPath,password, whitelist }) => {
|
||||||
|
return axios.post(`/responseSchema/${surveyPath}/validate`, {
|
||||||
|
password,
|
||||||
|
whitelist
|
||||||
|
})
|
||||||
|
}
|
138
web/src/render/components/VerifyWhiteDialog.vue
Normal file
138
web/src/render/components/VerifyWhiteDialog.vue
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="whiteVisible"
|
||||||
|
title="验证"
|
||||||
|
:show-close="false"
|
||||||
|
class="verify-white-wrap"
|
||||||
|
width="315"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
align-center
|
||||||
|
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="verify-white-head">
|
||||||
|
<div class="verify-white-title">验证</div>
|
||||||
|
<div v-if="whitelistTip" class="verify-white-tips">{{ whitelistTip }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="verify-white-body">
|
||||||
|
<el-input v-if="isPwd" v-model="state.password" class="wd255 mb16" placeholder="请输入6位字符串类型访问密码" />
|
||||||
|
<el-input v-if="isValue" v-model="state.value" class="wd255 mb16" :placeholder="placeholder" />
|
||||||
|
<div class="submit-btn" @click="handleSubmit">验证并开始答题</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref,reactive,computed,watch} from 'vue'
|
||||||
|
import { validate } from '../api/survey'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import 'element-plus/theme-chalk/src/message.scss'
|
||||||
|
|
||||||
|
const whiteVisible = ref(false)
|
||||||
|
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
|
const state = reactive({
|
||||||
|
password: '',
|
||||||
|
value: '',
|
||||||
|
is_submit:false
|
||||||
|
})
|
||||||
|
|
||||||
|
const baseConf = computed(() => store.state.baseConf || {})
|
||||||
|
const isPwd = computed(() => baseConf.value.passwordSwitch)
|
||||||
|
const whitelistType = computed(() => baseConf.value.whitelistType)
|
||||||
|
const memberType = computed(() => baseConf.value.memberType)
|
||||||
|
const whitelistTip = computed(() => baseConf.value.whitelistTip)
|
||||||
|
const surveyPath = computed(() => store.state?.surveyPath || '')
|
||||||
|
|
||||||
|
const isValue = computed(() => {
|
||||||
|
if(!whitelistType.value) return false
|
||||||
|
return whitelistType.value!='ALL'
|
||||||
|
})
|
||||||
|
|
||||||
|
const placeholder = computed(() => {
|
||||||
|
if (whitelistType.value == 'MEMBER') {
|
||||||
|
return '请输入用户名'
|
||||||
|
}
|
||||||
|
if(memberType.value == 'MOBILE'){
|
||||||
|
return '请输入手机号'
|
||||||
|
}
|
||||||
|
if(memberType.value == 'EMAIL'){
|
||||||
|
return '请输入邮箱'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSubmit = async() => {
|
||||||
|
if (state.is_submit) return;
|
||||||
|
const params = {
|
||||||
|
surveyPath:surveyPath.value
|
||||||
|
}
|
||||||
|
if (isValue.value) {
|
||||||
|
params.whitelist = state.value
|
||||||
|
}
|
||||||
|
if(isPwd.value){
|
||||||
|
params.password = state.password
|
||||||
|
}
|
||||||
|
const res = await validate(params)
|
||||||
|
if (res.code != 200) {
|
||||||
|
ElMessage.error(res.errmsg || '验证失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
whiteVisible.value = false
|
||||||
|
store.commit('setWhiteData',params)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(()=>baseConf.value, () => {
|
||||||
|
if (whiteVisible.value) return
|
||||||
|
if(isValue.value || isPwd.value){
|
||||||
|
whiteVisible.value = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.verify-white-wrap{
|
||||||
|
.verify-white-body{
|
||||||
|
padding:0 14px
|
||||||
|
}
|
||||||
|
.verify-white-head{
|
||||||
|
padding:0 14px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
margin-top:2px;
|
||||||
|
}
|
||||||
|
.mb16{
|
||||||
|
margin-bottom:16px;
|
||||||
|
}
|
||||||
|
.verify-white-tips{
|
||||||
|
text-align: center;
|
||||||
|
margin-top:8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #92949D;
|
||||||
|
}
|
||||||
|
.verify-white-title{
|
||||||
|
font-size: 16px;
|
||||||
|
color: #292A36;
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
.submit-btn{
|
||||||
|
background: #FAA600;
|
||||||
|
border-radius: 2px;
|
||||||
|
width:255px;
|
||||||
|
height:32px;
|
||||||
|
color:#fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top:4px;
|
||||||
|
margin-bottom:14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -79,7 +79,8 @@ const normalizationRequestBody = () => {
|
|||||||
surveyPath: surveyPath.value,
|
surveyPath: surveyPath.value,
|
||||||
data: JSON.stringify(formValues),
|
data: JSON.stringify(formValues),
|
||||||
difTime: Date.now() - enterTime,
|
difTime: Date.now() - enterTime,
|
||||||
clientTime: Date.now()
|
clientTime: Date.now(),
|
||||||
|
...whiteData.value
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encryptInfo?.encryptType) {
|
if (encryptInfo?.encryptType) {
|
||||||
|
@ -49,5 +49,8 @@ export default {
|
|||||||
},
|
},
|
||||||
setRuleEgine(state, ruleEngine) {
|
setRuleEgine(state, ruleEngine) {
|
||||||
state.ruleEngine = ruleEngine
|
state.ruleEngine = ruleEngine
|
||||||
|
},
|
||||||
|
setWhiteData(state, data) {
|
||||||
|
state.whiteData = data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,5 +12,8 @@ export default {
|
|||||||
questionSeq: [], // 题目的顺序,因为可能会有分页的情况,所以是一个二维数组[[qid1, qid2], [qid3,qid4]]
|
questionSeq: [], // 题目的顺序,因为可能会有分页的情况,所以是一个二维数组[[qid1, qid2], [qid3,qid4]]
|
||||||
voteMap: {},
|
voteMap: {},
|
||||||
encryptInfo: null,
|
encryptInfo: null,
|
||||||
ruleEngine: null
|
ruleEngine: null,
|
||||||
|
whiteData: {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user