Merge branch 'feature/pinia' into develop

# Conflicts:
#	web/src/management/components/LeftMenu.vue
#	web/src/management/pages/edit/components/ModuleNavbar.vue
#	web/src/management/pages/edit/modules/contentModule/PublishPanel.vue
#	web/src/management/pages/edit/modules/contentModule/SavePanel.vue
#	web/src/management/pages/edit/modules/resultModule/CatalogPanel.vue
#	web/src/management/pages/edit/modules/settingModule/SettingPanel.vue
#	web/src/management/pages/list/components/BaseList.vue
#	web/src/management/pages/list/components/SpaceList.vue
#	web/src/management/pages/list/components/SpaceModify.vue
#	web/src/management/pages/list/index.vue
#	web/src/management/router/index.ts
#	web/src/management/store/edit/getters.js
#	web/src/management/store/list/index.js
#	web/src/management/utils/index.js
#	web/src/materials/questions/widgets/BinaryChoiceModule/meta.js
#	web/src/materials/questions/widgets/CheckboxModule/meta.js
#	web/src/materials/questions/widgets/RadioModule/meta.js
#	web/src/materials/questions/widgets/VoteModule/meta.js
#	web/src/render/pages/RenderPage.vue
#	web/src/render/store/actions.js
#	web/src/render/store/mutations.js
#	web/src/render/store/state.js
This commit is contained in:
sudoooooo 2024-07-23 14:14:52 +08:00
commit cf495b60d1
86 changed files with 1681 additions and 1564 deletions

7
web/env.d.ts vendored
View File

@ -1,8 +1 @@
/// <reference types="vite/client" /> /// <reference types="vite/client" />
declare module "vuex" {
export * from "vuex/types/index.d.ts";
export * from "vuex/types/helpers.d.ts";
export * from "vuex/types/logger.d.ts";
export * from "vuex/types/vue.d.ts";
}

View File

@ -26,11 +26,11 @@
"moment": "^2.29.4", "moment": "^2.29.4",
"nanoid": "^5.0.7", "nanoid": "^5.0.7",
"node-forge": "^1.3.1", "node-forge": "^1.3.1",
"pinia": "^2.1.7",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"vue": "^3.4.15", "vue": "^3.4.15",
"vue-router": "^4.2.5", "vue-router": "^4.2.5",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"vuex": "^4.0.2",
"xss": "^1.0.14", "xss": "^1.0.14",
"yup": "^1.4.0" "yup": "^1.4.0"
}, },

View File

@ -24,13 +24,13 @@
<script setup> <script setup>
import { ref, shallowRef, onBeforeMount, watch, computed } from 'vue' import { ref, shallowRef, onBeforeMount, watch, computed } from 'vue'
import { useStore } from 'vuex'
import { get as _get } from 'lodash-es' import { get as _get } from 'lodash-es'
import '@wangeditor/editor/dist/css/style.css' import '@wangeditor/editor/dist/css/style.css'
import './styles/reset-wangeditor.scss' import './styles/reset-wangeditor.scss'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue' import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { useUserStore } from '@/management/stores/user'
import { replacePxWithRem } from './utils' import { replacePxWithRem } from './utils'
const emit = defineEmits(['input', 'onFocus', 'change', 'blur', 'created']) const emit = defineEmits(['input', 'onFocus', 'change', 'blur', 'created'])
@ -66,8 +66,8 @@ const editorConfig = {
MENU_CONF: {} MENU_CONF: {}
} }
const store = useStore() const userStore = useUserStore()
const token = _get(store, 'state.user.userInfo.token') const token = _get(userStore, 'userInfo.token')
// //
editorConfig.MENU_CONF['uploadImage'] = { editorConfig.MENU_CONF['uploadImage'] = {

View File

@ -1,4 +1,4 @@
// 静态数据 // test静态数据,实际业务里无用
export const ruleConf = [ export const ruleConf = [
{ {
conditions: [ conditions: [

View File

@ -1,7 +1,7 @@
import axios from 'axios' import axios from 'axios'
import store from '@/management/store/index'
import router from '@/management/router/index' import router from '@/management/router/index'
import { get as _get } from 'lodash-es' import { get as _get } from 'lodash-es'
import { useUserStore } from '../stores/user'
export const CODE_MAP = { export const CODE_MAP = {
SUCCESS: 200, SUCCESS: 200,
@ -36,8 +36,9 @@ instance.interceptors.response.use(
) )
instance.interceptors.request.use((config) => { instance.interceptors.request.use((config) => {
const hasLogined = _get(store, 'state.user.hasLogined') const userStore = useUserStore()
const token = _get(store, 'state.user.userInfo.token') const hasLogined = _get(userStore, 'hasLogined')
const token = _get(userStore, 'userInfo.token')
if (hasLogined && token) { if (hasLogined && token) {
if (!config.headers) { if (!config.headers) {
config.headers = {} config.headers = {}

View File

@ -16,11 +16,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed } from 'vue'
import { useStore } from 'vuex'
import { type IMember, type ListItem } from '@/management/utils/types/workSpace' import { type IMember, type ListItem } from '@/management/utils/types/workSpace'
import OperationSelect from './OperationSelect.vue' import OperationSelect from './OperationSelect.vue'
import { useWorkSpaceStore } from '@/management/stores/workSpace'
const store = useStore() const workSpaceStore = useWorkSpaceStore()
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
members: IMember[] members: IMember[]
@ -43,7 +43,7 @@ const list = computed({
} }
}) })
const currentUserId = computed(() => { const currentUserId = computed(() => {
return store.state.list.spaceDetail?.currentUserId return workSpaceStore.spaceDetail?.currentUserId
}) })
const handleRemove = (index: number) => { const handleRemove = (index: number) => {
list.value.splice(index, 1) list.value.splice(index, 1)

View File

@ -23,7 +23,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import { ref } from 'vue'
import { useStore } from 'vuex'
import MemberList from './MemberList.vue' import MemberList from './MemberList.vue'
import { getUserList } from '@/management/api/space' import { getUserList } from '@/management/api/space'
import { import {
@ -33,8 +32,9 @@ import {
roleLabels roleLabels
} from '@/management/utils/types/workSpace' } from '@/management/utils/types/workSpace'
import { CODE_MAP } from '@/management/api/base' import { CODE_MAP } from '@/management/api/base'
import { useUserStore } from '@/management/stores/user'
const store = useStore() const userStore = useUserStore()
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
members?: IMember[] members?: IMember[]
@ -65,7 +65,7 @@ const remoteMethod = async (query: string) => {
if (res.code === CODE_MAP.SUCCESS) { if (res.code === CODE_MAP.SUCCESS) {
selectOptions.value = res.data.map((item: any) => { selectOptions.value = res.data.map((item: any) => {
// //
const currentUser = item.username === store.state.user.userInfo.username const currentUser = item.username === userStore.userInfo?.username
return { return {
value: item.userId, value: item.userId,
label: item.username, label: item.username,

View File

@ -30,12 +30,12 @@
<script setup> <script setup>
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { useStore } from 'vuex' import { useEditStore } from '@/management/stores/edit'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()
import LogoIcon from './LogoIcon.vue' import LogoIcon from './LogoIcon.vue'
import { SurveyPermissions } from '@/management/utils/types/workSpace.ts' import { SurveyPermissions } from '@/management/utils/types/workSpace.ts'
const store = useStore() const editStore = useEditStore()
const tabArr = [ const tabArr = [
{ {
@ -62,7 +62,7 @@ const tabArr = [
] ]
const tabs = ref([]) const tabs = ref([])
watch( watch(
() => store.state.cooperPermissions, () => editStore.cooperPermissions,
(newVal) => { (newVal) => {
tabs.value = [] tabs.value = []
// //

View File

@ -1,17 +1,19 @@
import { computed } from 'vue' import { computed } from 'vue'
import store from '@/management/store' import { storeToRefs } from 'pinia'
import { useEditStore } from '@/management/stores/edit'
import { cleanRichText } from '@/common/xss' import { cleanRichText } from '@/common/xss'
export const useQuestionInfo = (field) => { export const useQuestionInfo = (field) => {
const editStore = useEditStore()
const { questionDataList } = storeToRefs(editStore)
const getQuestionTitle = computed(() => { const getQuestionTitle = computed(() => {
const questionDataList = store.state.edit.schema.questionDataList
return () => { return () => {
return questionDataList.find((item) => item.field === field)?.title return questionDataList.value.find((item) => item.field === field)?.title
} }
}) })
const getOptionTitle = computed(() => { const getOptionTitle = computed(() => {
const questionDataList = store.state.edit.schema.questionDataList
return (value) => { return (value) => {
const options = questionDataList.find((item) => item.field === field)?.options || [] const options = questionDataList.value.find((item) => item.field === field)?.options || []
if (value instanceof Array) { if (value instanceof Array) {
return options return options
.filter((item) => value.includes(item.hash)) .filter((item) => value.includes(item.hash))

View File

@ -1,14 +1,15 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import store from './store' import { createPinia } from 'pinia'
import plainText from './directive/plainText' import plainText from './directive/plainText'
import safeHtml from './directive/safeHtml' import safeHtml from './directive/safeHtml'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
const pinia = createPinia()
const app = createApp(App) const app = createApp(App)
app.use(store) app.use(pinia)
app.use(router) app.use(router)
app.use(plainText) app.use(plainText)

View File

@ -133,9 +133,6 @@ const onPreviewImage = (e) => {
} }
.table-row-cell { .table-row-cell {
white-space: nowrap; /* 禁止自动换行 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: ellipsis; /* 显示省略号 */
:deep(img) { :deep(img) {
height: 23px !important; height: 23px !important;
width: auto !important; width: auto !important;

View File

@ -38,13 +38,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed, toRefs } from 'vue' import { ref, reactive, computed, toRefs } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss' import 'element-plus/theme-chalk/src/message.scss'
import { createSurvey } from '@/management/api/survey' import { createSurvey } from '@/management/api/survey'
import { SURVEY_TYPE_LIST } from '../types' import { SURVEY_TYPE_LIST } from '../types'
import { useWorkSpaceStore } from '@/management/stores/workSpace'
interface Props { interface Props {
selectType?: string selectType?: string
@ -54,6 +52,7 @@ const props = withDefaults(defineProps<Props>(), {
selectType: 'normal' selectType: 'normal'
}) })
const workSpaceStore = useWorkSpaceStore()
const ruleForm = ref<any>(null) const ruleForm = ref<any>(null)
const state = reactive({ const state = reactive({
@ -79,7 +78,6 @@ const checkForm = (fn: Function) => {
} }
const router = useRouter() const router = useRouter()
const store = useStore()
const submit = () => { const submit = () => {
if (!state.canSubmit) { if (!state.canSubmit) {
return return
@ -94,8 +92,8 @@ const submit = () => {
surveyType: selectType, surveyType: selectType,
...state.form ...state.form
} }
if (store.state.list.workSpaceId) { if (workSpaceStore.workSpaceId) {
payload.workspaceId = store.state.list.workSpaceId payload.workspaceId = workSpaceStore.workSpaceId
} }
const res: any = await createSurvey(payload) const res: any = await createSurvey(payload)
if (res?.code === 200 && res?.data?.id) { if (res?.code === 200 && res?.data?.id) {

View File

@ -35,7 +35,7 @@
<script> <script>
import { computed, defineComponent, ref, getCurrentInstance } from 'vue' import { computed, defineComponent, ref, getCurrentInstance } from 'vue'
import { useStore } from 'vuex' import { useEditStore } from '@/management/stores/edit'
import QuestionContainerB from '@/materials/questions/QuestionContainerB' import QuestionContainerB from '@/materials/questions/QuestionContainerB'
import QuestionWrapper from '@/management/pages/edit/components/QuestionWrapper.vue' import QuestionWrapper from '@/management/pages/edit/components/QuestionWrapper.vue'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
@ -62,13 +62,13 @@ export default defineComponent({
}, },
emits: ['change', 'select', 'changeSeq'], emits: ['change', 'select', 'changeSeq'],
setup(props, { emit }) { setup(props, { emit }) {
const store = useStore() const editStore = useEditStore()
const renderData = computed({ const renderData = computed({
get() { get() {
return filterQuestionPreviewData(props.questionDataList) return filterQuestionPreviewData(props.questionDataList)
}, },
set(questionDataList) { set(value) {
store.commit('edit/setQuestionDataList', questionDataList) editStore.setQuestionDataList(value)
} }
}) })
const handleSelect = (index) => { const handleSelect = (index) => {

View File

@ -28,8 +28,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useStore } from 'vuex' import { useEditStore } from '@/management/stores/edit'
import { get as _get } from 'lodash-es'
import { showLogicEngine } from '@/management/hooks/useShowLogicEngine' import { showLogicEngine } from '@/management/hooks/useShowLogicEngine'
@ -42,8 +41,9 @@ import SavePanel from '../modules/contentModule/SavePanel.vue'
import PublishPanel from '../modules/contentModule/PublishPanel.vue' import PublishPanel from '../modules/contentModule/PublishPanel.vue'
import CooperationPanel from '../modules/contentModule/CooperationPanel.vue' import CooperationPanel from '../modules/contentModule/CooperationPanel.vue'
const store = useStore() const editStore = useEditStore()
const title = computed(() => _get(store.state, 'edit.schema.metaData.title')) const { schema, changeSchema } = editStore
const title = computed(() => (editStore.schema?.metaData as any)?.title || '')
// - // -
const updateLogicConf = () => { const updateLogicConf = () => {
let res = { let res = {
@ -68,7 +68,7 @@ const updateLogicConf = () => {
const showLogicConf = showLogicEngine.value.toJson() const showLogicConf = showLogicEngine.value.toJson()
// //
store.dispatch('edit/changeSchema', { key: 'logicConf', value: { showLogicConf } }) changeSchema({ key: 'logicConf', value: { showLogicConf } })
return res return res
} }
@ -82,7 +82,7 @@ const updateWhiteConf = () => {
validated: true, validated: true,
message: '' message: ''
} }
const baseConf = store.state.edit.schema.baseConf || {} const baseConf = (schema?.baseConf as any) || {}
if (baseConf.passwordSwitch && !baseConf.password) { if (baseConf.passwordSwitch && !baseConf.password) {
res = { res = {
validated: false, validated: false,

View File

@ -15,7 +15,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted } from 'vue' import { onMounted } from 'vue'
import { useStore } from 'vuex' import { useEditStore } from '@/management/stores/edit'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss' import 'element-plus/theme-chalk/src/message.scss'
@ -26,16 +26,17 @@ import Navbar from './components/ModuleNavbar.vue'
import { initShowLogicEngine } from '@/management/hooks/useShowLogicEngine' import { initShowLogicEngine } from '@/management/hooks/useShowLogicEngine'
const store = useStore() const editStore = useEditStore()
const { schema, init, setSurveyId } = editStore
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
onMounted(async () => { onMounted(async () => {
store.commit('edit/setSurveyId', route.params.id) setSurveyId(route.params.id as string)
try { try {
await store.dispatch('edit/init') await init()
await initShowLogicEngine(store.state.edit.schema.logicConf.showLogicConf || {}) await initShowLogicEngine(schema.logicConf.showLogicConf || {})
} catch (err: any) { } catch (err: any) {
ElMessage.error(err.message) ElMessage.error(err.message)

View File

@ -25,9 +25,9 @@
</el-popover> </el-popover>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch } from 'vue' import { ref, watch } from 'vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { get as _get } from 'lodash-es' import { useEditStore } from '@/management/stores/edit'
import moment from 'moment' import moment from 'moment'
import 'moment/locale/zh-cn' import 'moment/locale/zh-cn'
moment.locale('zh-cn') moment.locale('zh-cn')
@ -44,21 +44,21 @@ const publishList = ref<Array<any>>([])
const currentTab = ref<'daily' | 'publish'>('daily') const currentTab = ref<'daily' | 'publish'>('daily')
const visible = ref<boolean>(false) const visible = ref<boolean>(false)
const store = useStore() const editStore = useEditStore()
const { surveyId, schemaUpdateTime } = storeToRefs(editStore)
const queryHistories = async () => { const queryHistories = async () => {
if (dirtyMonitor.value) { if (dirtyMonitor.value) {
loading.value = true loading.value = true
dirtyMonitor.value = false dirtyMonitor.value = false
const surveyId = _get(store.state, 'edit.surveyId')
const [dHis, pHis] = await Promise.all([ const [dHis, pHis] = await Promise.all([
getSurveyHistory({ getSurveyHistory({
surveyId, surveyId: surveyId.value,
historyType: 'dailyHis' historyType: 'dailyHis'
}), }),
getSurveyHistory({ getSurveyHistory({
surveyId, surveyId: surveyId.value,
historyType: 'publishHis' historyType: 'publishHis'
}) })
]).finally(() => { ]).finally(() => {
@ -81,7 +81,6 @@ const handlePopoverShow = async () => {
} }
const loading = ref<boolean>(false) const loading = ref<boolean>(false)
const dirtyMonitor = ref<boolean>(true) const dirtyMonitor = ref<boolean>(true)
const schemaUpdateTime = computed(() => _get(store.state, 'edit.schemaUpdateTime'))
watch( watch(
schemaUpdateTime, schemaUpdateTime,

View File

@ -5,7 +5,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { useStore } from 'vuex' import { useEditStore } from '@/management/stores/edit'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss' import 'element-plus/theme-chalk/src/message.scss'
@ -21,7 +21,8 @@ interface Props {
const props = defineProps<Props>() const props = defineProps<Props>()
const isPublishing = ref<boolean>(false) const isPublishing = ref<boolean>(false)
const store = useStore() const editStore = useEditStore()
const { schema, getSchemaFromRemote } = editStore
const router = useRouter() const router = useRouter()
const validate = () => { const validate = () => {
@ -59,7 +60,7 @@ const handlePublish = async () => {
return return
} }
const saveData = buildData(store.state.edit.schema) const saveData = buildData(schema)
if (!saveData.surveyId) { if (!saveData.surveyId) {
isPublishing.value = false isPublishing.value = false
ElMessage.error('未获取到问卷id') ElMessage.error('未获取到问卷id')
@ -77,7 +78,7 @@ const handlePublish = async () => {
const publishRes: any = await publishSurvey({ surveyId: saveData.surveyId }) const publishRes: any = await publishSurvey({ surveyId: saveData.surveyId })
if (publishRes.code === 200) { if (publishRes.code === 200) {
ElMessage.success('发布成功') ElMessage.success('发布成功')
store.dispatch('edit/getSchemaFromRemote') getSchemaFromRemote()
router.push({ name: 'publish' }) router.push({ name: 'publish' })
} else { } else {
ElMessage.error(`发布失败 ${publishRes.errmsg}`) ElMessage.error(`发布失败 ${publishRes.errmsg}`)

View File

@ -15,10 +15,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, nextTick, watch } from 'vue' import { ref, computed, nextTick, watch } from 'vue'
import { useStore } from 'vuex'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { storeToRefs } from 'pinia'
import { get as _get } from 'lodash-es' import { useEditStore } from '@/management/stores/edit'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss' import 'element-plus/theme-chalk/src/message.scss'
@ -44,7 +43,9 @@ const saveText = computed(
})[autoSaveStatus.value] })[autoSaveStatus.value]
) )
const store = useStore() const editStore = useEditStore()
const { schemaUpdateTime } = storeToRefs(editStore)
const { schema } = editStore
const validate = () => { const validate = () => {
let checked = true let checked = true
@ -68,7 +69,7 @@ const validate = () => {
} }
const saveData = async () => { const saveData = async () => {
const saveData = buildData(store.state.edit.schema) const saveData = buildData(schema)
if (!saveData.surveyId) { if (!saveData.surveyId) {
ElMessage.error('未获取到问卷id') ElMessage.error('未获取到问卷id')
@ -144,7 +145,6 @@ const handleSave = async () => {
} }
} }
const schemaUpdateTime = computed(() => _get(store.state, 'edit.schemaUpdateTime'))
watch(schemaUpdateTime, () => { watch(schemaUpdateTime, () => {
triggerAutoSave() triggerAutoSave()
}) })

View File

@ -6,13 +6,13 @@
:bannerConf="bannerConf" :bannerConf="bannerConf"
:readonly="false" :readonly="false"
:is-selected="currentEditOne === 'mainTitle'" :is-selected="currentEditOne === 'mainTitle'"
@select="onSelectEditOne('mainTitle')" @select="setCurrentEditOne('mainTitle')"
@change="handleChange" @change="handleChange"
/> />
<MaterialGroup <MaterialGroup
:current-edit-one="parseInt(currentEditOne)" :current-edit-one="parseInt(currentEditOne)"
:questionDataList="questionDataList" :questionDataList="questionDataList"
@select="onSelectEditOne" @select="setCurrentEditOne"
@change="handleChange" @change="handleChange"
@changeSeq="onQuestionOperation" @changeSeq="onQuestionOperation"
ref="materialGroup" ref="materialGroup"
@ -22,7 +22,7 @@
:readonly="false" :readonly="false"
:skin-conf="skinConf" :skin-conf="skinConf"
:is-selected="currentEditOne === 'submit'" :is-selected="currentEditOne === 'submit'"
@select="onSelectEditOne('submit')" @select="setCurrentEditOne('submit')"
/> />
</div> </div>
</div> </div>
@ -30,34 +30,29 @@
</template> </template>
<script setup> <script setup>
import { computed, ref, watch } from 'vue' import { ref, watch, toRefs } from 'vue'
import communalLoader from '@materials/communals/communalLoader.js' import communalLoader from '@materials/communals/communalLoader.js'
import MaterialGroup from '@/management/pages/edit/components/MaterialGroup.vue' import MaterialGroup from '@/management/pages/edit/components/MaterialGroup.vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { useEditStore } from '@/management/stores/edit'
const MainTitle = communalLoader.loadComponent('MainTitle') const MainTitle = communalLoader.loadComponent('MainTitle')
const SubmitButton = communalLoader.loadComponent('SubmitButton') const SubmitButton = communalLoader.loadComponent('SubmitButton')
const store = useStore() const editStore = useEditStore()
const { questionDataList, currentEditOne, currentEditKey } = storeToRefs(editStore)
const { schema, changeSchema, moveQuestion, copyQuestion, deleteQuestion, setCurrentEditOne } =
editStore
const mainOperation = ref(null) const mainOperation = ref(null)
const materialGroup = ref(null) const materialGroup = ref(null)
const bannerConf = computed(() => store.state.edit.schema.bannerConf) const { bannerConf, submitConf, skinConf } = toRefs(schema)
const submitConf = computed(() => store.state.edit.schema.submitConf) // const autoScrollData = computed(() => {
const skinConf = computed(() => store.state.edit.schema.skinConf) // return {
const questionDataList = computed(() => store.state.edit.schema.questionDataList) // currentEditOne: currentEditOne.value,
const currentEditOne = computed(() => store.state.edit.currentEditOne) // len: questionDataList.value.length
const currentEditKey = computed(() => store.getters['edit/currentEditKey']) // }
const autoScrollData = computed(() => { // })
return {
currentEditOne: currentEditOne.value,
len: questionDataList.value.length
}
})
const onSelectEditOne = async (currentEditOne) => {
store.commit('edit/setCurrentEditOne', currentEditOne)
}
const handleChange = (data) => { const handleChange = (data) => {
if (currentEditOne.value === null) { if (currentEditOne.value === null) {
@ -65,34 +60,33 @@ const handleChange = (data) => {
} }
const { key, value } = data const { key, value } = data
const resultKey = `${currentEditKey.value}.${key}` const resultKey = `${currentEditKey.value}.${key}`
store.dispatch('edit/changeSchema', { key: resultKey, value }) changeSchema({ key: resultKey, value })
} }
const onMainClick = (e) => { const onMainClick = (e) => {
if (e.target === mainOperation.value) { if (e.target === mainOperation.value) {
store.commit('edit/setCurrentEditOne', null) setCurrentEditOne(null)
} }
} }
const onQuestionOperation = (data) => { const onQuestionOperation = (data) => {
switch (data.type) { switch (data.type) {
case 'move': case 'move':
store.dispatch('edit/moveQuestion', { moveQuestion({
index: data.index, index: data.index,
range: data.range range: data.range
}) })
break break
case 'delete': case 'delete':
store.dispatch('edit/deleteQuestion', { index: data.index }) deleteQuestion({ index: data.index })
break break
case 'copy': case 'copy':
store.dispatch('edit/copyQuestion', { index: data.index }) copyQuestion({ index: data.index })
break break
default: default:
break break
} }
} }
watch( watch(
skinConf, skinConf,
(newVal) => { (newVal) => {
@ -114,22 +108,24 @@ watch(
} }
) )
watch(autoScrollData, (newVal) => { //
const { currentEditOne } = newVal //
if (typeof currentEditOne === 'number') { // watch(autoScrollData, (newVal) => {
setTimeout(() => { // const { currentEditOne } = newVal
const field = questionDataList.value?.[currentEditOne]?.field // if (typeof currentEditOne === 'number') {
if (field) { // setTimeout(() => {
const questionModule = materialGroup.value?.getQuestionRefByField(field) // const field = questionDataList.value?.[currentEditOne]?.field
if (questionModule && questionModule.$el) { // if (field) {
questionModule.$el.scrollIntoView({ // const questionModule = materialGroup.value?.getQuestionRefByField(field)
behavior: 'smooth' // if (questionModule && questionModule.$el) {
}) // questionModule.$el.scrollIntoView({
} // behavior: 'smooth'
} // })
}, 0) // }
} // }
}) // }, 0)
// }
// })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -17,23 +17,19 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { storeToRefs } from 'pinia'
import { useStore } from 'vuex' import { useEditStore } from '@/management/stores/edit'
import SetterField from '@/management/pages/edit/components/SetterField.vue' import SetterField from '@/management/pages/edit/components/SetterField.vue'
const store = useStore() const editStore = useEditStore()
const { currentEditOne, currentEditKey, currentEditMeta, formConfigList, moduleConfig } =
const currentEditOne = computed(() => store.state?.edit?.currentEditOne) storeToRefs(editStore)
const formConfigList = computed(() => store.getters['edit/formConfigList']) const { changeSchema } = editStore
const moduleConfig = computed(() => store.getters['edit/moduleConfig'])
const currentEditKey = computed(() => store.getters['edit/currentEditKey'])
const currentEditMeta = computed(() => store.getters['edit/currentEditMeta'])
const handleFormChange = (data: any) => { const handleFormChange = (data: any) => {
const { key, value } = data const { key, value } = data
const resultKey = `${currentEditKey.value}.${key}` const resultKey = `${currentEditKey.value}.${key}`
store.dispatch('edit/changeSchema', { key: resultKey, value }) changeSchema({ key: resultKey, value })
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -12,7 +12,7 @@
:title="element.title" :title="element.title"
:indexNumber="element.indexNumber" :indexNumber="element.indexNumber"
:showIndex="element.showIndex" :showIndex="element.showIndex"
@select="handleSelect(index)" @select="setCurrentEditOne(index)"
/> />
</template> </template>
</draggable> </draggable>
@ -20,34 +20,30 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { useEditStore } from '@/management/stores/edit'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import CatalogItem from './CatalogItem.vue' import CatalogItem from './CatalogItem.vue'
import { filterQuestionPreviewData } from '@/management/utils/index' import { filterQuestionPreviewData } from '@/management/utils/index'
const store = useStore() const editStore = useEditStore()
const { questionDataList, currentEditOne } = storeToRefs(editStore)
const { moveQuestion, setCurrentEditOne } = editStore
const renderData = computed(() => { const renderData = computed(() => {
const questions = store.state.edit.schema.questionDataList return filterQuestionPreviewData(questionDataList.value) || []
return filterQuestionPreviewData(questions) || []
}) })
const handleDragEnd = ({ newIndex, oldIndex }: any) => { const handleDragEnd = ({ newIndex, oldIndex }: any) => {
const currentActivityKey = store.state.edit.currentEditOne if (currentEditOne.value === oldIndex) {
setCurrentEditOne(newIndex)
if (currentActivityKey === oldIndex) {
handleSelect(newIndex)
} }
store.dispatch('edit/moveQuestion', { moveQuestion({
index: oldIndex, index: oldIndex,
range: newIndex - oldIndex range: newIndex - oldIndex
}) })
} }
const handleSelect = (idx: number) => {
store.commit('edit/setCurrentEditOne', idx)
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.question-catalog-wrapper { .question-catalog-wrapper {

View File

@ -45,20 +45,24 @@ import { DND_GROUP } from '@/management/config/dnd'
import questionMenuConfig, { questionTypeList } from '@/management/config/questionMenuConfig' import questionMenuConfig, { questionTypeList } from '@/management/config/questionMenuConfig'
import { getQuestionByType } from '@/management/utils/index' import { getQuestionByType } from '@/management/utils/index'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { get as _get, isNumber as _isNumber } from 'lodash-es' import { useEditStore } from '@/management/stores/edit'
import { isNumber as _isNumber } from 'lodash-es'
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { QUESTION_TYPE } from '@/common/typeEnum.ts' import { QUESTION_TYPE } from '@/common/typeEnum.ts'
const store = useStore()
const editStore = useEditStore()
const { questionDataList, currentEditOne } = storeToRefs(editStore)
const { addQuestion, setCurrentEditOne } = editStore
const activeNames = ref([0, 1]) const activeNames = ref([0, 1])
const previewImg = ref('') const previewImg = ref('')
const isShowPreviewImage = ref(false) const isShowPreviewImage = ref(false)
const previewTop = ref(0) const previewTop = ref(0)
const questionDataList = computed(() => _get(store, 'state.edit.schema.questionDataList'))
const newQuestionIndex = computed(() => { const newQuestionIndex = computed(() => {
const currentEditOne = _get(store, 'state.edit.currentEditOne') const index = _isNumber(currentEditOne.value)
const index = _isNumber(currentEditOne) ? currentEditOne + 1 : questionDataList.value.length ? currentEditOne.value + 1
: questionDataList.value.length
return index return index
}) })
@ -78,8 +82,8 @@ const getNewQuestion = ({ type }) => {
const onQuestionType = ({ type }) => { const onQuestionType = ({ type }) => {
const newQuestion = getNewQuestion({ type }) const newQuestion = getNewQuestion({ type })
store.dispatch('edit/addQuestion', { question: newQuestion, index: newQuestionIndex.value }) addQuestion({ question: newQuestion, index: newQuestionIndex.value })
store.commit('edit/setCurrentEditOne', newQuestionIndex.value) setCurrentEditOne(newQuestionIndex.value)
} }
const showPreview = ({ snapshot }, id) => { const showPreview = ({ snapshot }, id) => {

View File

@ -17,10 +17,13 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { EDIT_STATUS_MAP } from './components/enum'
const store = useStore() import { EDIT_STATUS_MAP } from './components/enum'
import { useEditStore } from '@/management/stores/edit'
const editStore = useEditStore()
const { currentEditStatus } = storeToRefs(editStore)
const statusList = [ const statusList = [
{ {
type: EDIT_STATUS_MAP.SUCCESS, type: EDIT_STATUS_MAP.SUCCESS,
@ -35,10 +38,8 @@ const statusList = [
] ]
const handleChangePreview = (data: any) => { const handleChangePreview = (data: any) => {
const currentStatus = store.state?.edit?.currentEditStatus if (currentEditStatus.value !== data.type) {
editStore.changeCurrentEditStatus(data.type)
if (currentStatus && currentStatus !== data.type) {
store.commit('edit/changeStatusPreview', data)
} }
} }
</script> </script>

View File

@ -12,9 +12,9 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { toRef } from 'vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { get as _get } from 'lodash-es' import { useEditStore } from '@/management/stores/edit'
import SuccessContent from './components/SuccessContent.vue' import SuccessContent from './components/SuccessContent.vue'
import OverTime from './components/OverTime.vue' import OverTime from './components/OverTime.vue'
@ -25,11 +25,10 @@ const components = {
[EDIT_STATUS_MAP.OVERTIME]: OverTime [EDIT_STATUS_MAP.OVERTIME]: OverTime
} }
const store = useStore() const editStore = useEditStore()
const currentEditStatus = computed(() => store.state?.edit?.currentEditStatus) const { currentEditStatus } = storeToRefs(editStore)
const moduleConfig = computed(() => { const { schema } = editStore
return _get(store.state, 'edit.schema.submitConf') const moduleConfig = toRef(schema, 'submitConf')
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.result-config-preview { .result-config-preview {

View File

@ -23,8 +23,9 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, shallowRef } from 'vue' import { computed, ref, shallowRef, toRef } from 'vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { useEditStore } from '@/management/stores/edit'
import { get as _get } from 'lodash-es' import { get as _get } from 'lodash-es'
import FormItem from '@/materials/setters/widgets/FormItem.vue' import FormItem from '@/materials/setters/widgets/FormItem.vue'
@ -36,15 +37,16 @@ const textMap = {
OverTime: '问卷过期页面配置' OverTime: '问卷过期页面配置'
} }
const store = useStore() const editStore = useEditStore()
const { currentEditStatus } = storeToRefs(editStore)
const { schema, changeSchema } = editStore
const components = shallowRef<any>({}) const components = shallowRef<any>({})
const registerTypes = ref<any>({}) const registerTypes = ref<any>({})
const moduleConfig = computed(() => _get(store.state, 'edit.schema.submitConf')) const moduleConfig = toRef(schema, 'submitConf')
const currentEditText = computed(() => (textMap as any)[store.state.edit.currentEditStatus]) const currentEditText = computed(() => (textMap as any)[currentEditStatus.value])
const formFields = computed(() => { const formFields = computed(() => {
const currentStatus = store.state.edit.currentEditStatus const formList = (statusConfig as any)[currentEditStatus.value] || []
const formList = (statusConfig as any)[currentStatus] || []
const list = formList.map((item: any) => { const list = formList.map((item: any) => {
const value = _get(moduleConfig.value, item.key, item.value) const value = _get(moduleConfig.value, item.key, item.value)
@ -57,7 +59,7 @@ const formFields = computed(() => {
}) })
const handleFormChange = ({ key, value }: any) => { const handleFormChange = ({ key, value }: any) => {
store.dispatch('edit/changeSchema', { changeSchema({
key: `submitConf.${key}`, key: `submitConf.${key}`,
value value
}) })

View File

@ -38,13 +38,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, onMounted, shallowRef } from 'vue' import { computed, ref, onMounted, shallowRef } from 'vue'
import { useStore } from 'vuex'
import { import {
cloneDeep as _cloneDeep, cloneDeep as _cloneDeep,
isArray as _isArray, isArray as _isArray,
get as _get, get as _get,
isFunction as _isFunction isFunction as _isFunction
} from 'lodash-es' } from 'lodash-es'
import { useEditStore } from '@/management/stores/edit'
import baseConfig from '@/management/pages/edit/setterConfig/baseConfig' import baseConfig from '@/management/pages/edit/setterConfig/baseConfig'
import baseFormConfig from '@/management/pages/edit/setterConfig/baseFormConfig' import baseFormConfig from '@/management/pages/edit/setterConfig/baseFormConfig'
@ -54,6 +54,9 @@ import setterLoader from '@/materials/setters/setterLoader'
import WhiteList from './components/WhiteList.vue' import WhiteList from './components/WhiteList.vue'
import TeamMemberList from './components/TeamMemberList.vue' import TeamMemberList from './components/TeamMemberList.vue'
const editStore = useEditStore()
const { schema, changeSchema } = editStore
const formConfigList = ref<Array<any>>([]) const formConfigList = ref<Array<any>>([])
const components = shallowRef<any>({ const components = shallowRef<any>({
['WhiteList']: WhiteList, ['WhiteList']: WhiteList,
@ -64,8 +67,8 @@ const registerTypes = ref<any>({
WhiteList: 'WhiteList', WhiteList: 'WhiteList',
TeamMemberList: 'TeamMemberList' TeamMemberList: 'TeamMemberList'
}) })
const store = useStore()
const schemaBaseConf = computed(() => store.state.edit?.schema?.baseConf || {}) const schemaBaseConf = computed(() => schema?.baseConf || {})
const setterList = computed(() => { const setterList = computed(() => {
const list = _cloneDeep(formConfigList.value) const list = _cloneDeep(formConfigList.value)
@ -79,12 +82,12 @@ const setterList = computed(() => {
if (_isArray(formKey)) { if (_isArray(formKey)) {
formValue = [] formValue = []
for (const key of formKey) { for (const key of formKey) {
const val = _get(store.state.edit.schema, key, formItem.value) const val = _get(schema, key, formItem.value)
formValue.push(val) formValue.push(val)
dataConfig[key] = val dataConfig[key] = val
} }
} else { } else {
formValue = _get(store.state.edit.schema, formKey, formItem.value) formValue = _get(schema, formKey, formItem.value)
dataConfig[formKey] = formValue dataConfig[formKey] = formValue
} }
formItem.value = formValue formItem.value = formValue
@ -103,9 +106,8 @@ const setterList = computed(() => {
}) })
}) })
const handleFormChange = (data: any) => { const handleFormChange = (data: any) => {
store.dispatch('edit/changeSchema', { changeSchema({
key: data.key, key: data.key,
value: data.value value: data.value
}) })
@ -173,7 +175,6 @@ onMounted(async () => {
padding-bottom: 19px; padding-bottom: 19px;
margin-bottom: 10px; margin-bottom: 10px;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
padding-left: 30px;
span { span {
position: relative; position: relative;

View File

@ -21,7 +21,7 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, computed, defineEmits, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant' import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
import { getMemberList } from '@/management/api/space' import { getMemberList } from '@/management/api/space'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'

View File

@ -27,13 +27,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useStore } from 'vuex' import { useEditStore } from '@/management/stores/edit'
import skinPresets from '@/management/config/skinPresets.js' import skinPresets from '@/management/config/skinPresets.js'
const store = useStore() const editStore = useEditStore()
const { changeThemePreset } = editStore
const groupName = ref<string>('temp') const groupName = ref<string>('temp')
const bannerList = computed(() => store?.state?.bannerList || []) const bannerList = computed(() => editStore.bannerList || [])
const groupList = computed(() => const groupList = computed(() =>
Object.keys(bannerList.value).map((key) => ({ Object.keys(bannerList.value).map((key) => ({
label: bannerList.value[key].name, label: bannerList.value[key].name,
@ -81,7 +82,7 @@ const changePreset = (banner: any) => {
presets = Object.assign(presets, (skinPresets as any)[name]) presets = Object.assign(presets, (skinPresets as any)[name])
} }
store.dispatch('edit/changeThemePreset', presets) changeThemePreset(presets)
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -108,7 +109,10 @@ const changePreset = (banner: any) => {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
.tag { .tag {
margin: 5px 8px; width: 55px;
margin: 5px 2px;
cursor: pointer;
flex: auto;
cursor: pointer; cursor: pointer;
&.current { &.current {
color: $primary-color; color: $primary-color;

View File

@ -24,9 +24,10 @@
</div> </div>
</template> </template>
<script> <script>
import { computed, defineComponent } from 'vue' import { defineComponent, toRefs } from 'vue'
import MaterialGroup from '@/management/pages/edit/components/MaterialGroup.vue' import MaterialGroup from '@/management/pages/edit/components/MaterialGroup.vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { useEditStore } from '@/management/stores/edit'
import communalLoader from '@materials/communals/communalLoader.js' import communalLoader from '@materials/communals/communalLoader.js'
const HeaderContent = () => communalLoader.loadComponent('HeaderContent') const HeaderContent = () => communalLoader.loadComponent('HeaderContent')
@ -43,15 +44,10 @@ export default defineComponent({
LogoIcon: LogoIcon() LogoIcon: LogoIcon()
}, },
setup() { setup() {
const store = useStore() const editStore = useEditStore()
const { questionDataList, currentEditOne, currentEditKey } = storeToRefs(editStore)
const bannerConf = computed(() => store.state.edit.schema.bannerConf) const { schema } = editStore
const submitConf = computed(() => store.state.edit.schema.submitConf) const { bannerConf, submitConf, skinConf, bottomConf } = toRefs(schema)
const bottomConf = computed(() => store.state.edit.schema.bottomConf)
const skinConf = computed(() => store.state.edit.schema.skinConf)
const questionDataList = computed(() => store.state.edit.schema.questionDataList)
const currentEditOne = computed(() => store.state.edit.currentEditOne)
const currentEditKey = computed(() => store.getters['edit/currentEditKey'])
return { return {
bannerConf, bannerConf,

View File

@ -24,23 +24,23 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { ref } from 'vue'
import { useStore } from 'vuex' import { useEditStore } from '@/management/stores/edit'
import { get as _get } from 'lodash-es' import { get as _get } from 'lodash-es'
import skinConfig from '@/management/pages/edit/setterConfig/skinConfig' import skinConfig from '@/management/pages/edit/setterConfig/skinConfig'
import SetterField from '@/management/pages/edit/components/SetterField.vue' import SetterField from '@/management/pages/edit/components/SetterField.vue'
const store = useStore() const editStore = useEditStore()
const { schema, changeSchema } = editStore
const collapse = ref<string>('') const collapse = ref<string>('')
const schema = computed(() => _get(store.state, 'edit.schema'))
const handleFormChange = (data: any, collapse: string) => { const handleFormChange = (data: any, collapse: string) => {
const { key, value } = data const { key, value } = data
const currentEditKey = `${collapse}` const currentEditKey = `${collapse}`
const resultKey = `${currentEditKey}.${key}` const resultKey = `${currentEditKey}.${key}`
store.dispatch('edit/changeSchema', { key: resultKey, value }) changeSchema({ key: resultKey, value })
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -5,17 +5,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, provide } from 'vue' import { computed, provide } from 'vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { useEditStore } from '@/management/stores/edit'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import RulePanel from '../../modules/logicModule/RulePanel.vue' import RulePanel from '../../modules/logicModule/RulePanel.vue'
import { filterQuestionPreviewData } from '@/management/utils/index' import { filterQuestionPreviewData } from '@/management/utils/index'
const store = useStore() const editStore = useEditStore()
const { questionDataList } = storeToRefs(editStore)
const questionDataList = computed(() => {
return store.state.edit.schema.questionDataList
})
const renderData = computed(() => { const renderData = computed(() => {
return filterQuestionPreviewData(cloneDeep(questionDataList.value)) return filterQuestionPreviewData(cloneDeep(questionDataList.value))

View File

@ -13,17 +13,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted } from 'vue' import { onMounted } from 'vue'
import { useStore } from 'vuex' import { useEditStore } from '@/management/stores/edit'
import CommonTemplate from '../../components/CommonTemplate.vue' import CommonTemplate from '../../components/CommonTemplate.vue'
import CatalogPanel from '../../modules/skinModule/CatalogPanel.vue' import CatalogPanel from '../../modules/skinModule/CatalogPanel.vue'
import PreviewPanel from '../../modules/skinModule/PreviewPanel.vue' import PreviewPanel from '../../modules/skinModule/PreviewPanel.vue'
import SetterPanel from '../../modules/skinModule/SetterPanel.vue' import SetterPanel from '../../modules/skinModule/SetterPanel.vue'
const store = useStore() const editStore = useEditStore()
onMounted(() => { onMounted(() => {
store.dispatch('getBannerData') editStore.fetchBannerData()
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -8,11 +8,17 @@ export default [
title: '提交限制', title: '提交限制',
key: 'limitConfig', key: 'limitConfig',
formList: ['limit_tLimit'] formList: ['limit_tLimit']
}, { },
{
title: '作答限制', title: '作答限制',
key: 'respondConfig', key: 'respondConfig',
formList: ['interview_pwd','answer_type','white_placeholder','white_list','team_list'] formList: [
'interview_pwd_switch',
'interview_pwd',
'answer_type',
'white_placeholder',
'white_list',
'team_list'
]
} }
] ]

View File

@ -22,12 +22,18 @@ export default {
type: 'QuestionTimeHour', type: 'QuestionTimeHour',
placement: 'top' placement: 'top'
}, },
interview_pwd: { interview_pwd_switch: {
keys: ['baseConf.passwordSwitch', 'baseConf.password'], key: 'baseConf.passwordSwitch',
label: '访问密码', label: '访问密码',
type: 'SwitchInput', type: 'CustomedSwitch'
},
interview_pwd: {
type: 'InputSetter',
placeholder: '请输入6位字符串类型访问密码 ', placeholder: '请输入6位字符串类型访问密码 ',
maxLength: 6 maxLength: 6,
relyFunc: (data) => {
return !!data?.passwordSwitch
}
}, },
answer_type: { answer_type: {
key: 'baseConf.whitelistType', key: 'baseConf.whitelistType',
@ -52,7 +58,7 @@ export default {
key: 'baseConf.whitelistTip', key: 'baseConf.whitelistTip',
label: '名单登录提示语', label: '名单登录提示语',
placeholder: '请输入名单提示语', placeholder: '请输入名单提示语',
type: 'InputWordLimit', type: 'InputSetter',
maxLength: 40, maxLength: 40,
relyFunc: (data) => { relyFunc: (data) => {
return ['CUSTOM', 'MEMBER'].includes(data.whitelistType) return ['CUSTOM', 'MEMBER'].includes(data.whitelistType)

View File

@ -105,9 +105,9 @@
<script setup> <script setup>
import { ref, computed, unref } from 'vue' import { ref, computed, unref } from 'vue'
import { useStore } from 'vuex'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { get, map } from 'lodash-es' import { get, map } from 'lodash-es'
import { storeToRefs } from 'pinia'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss' import 'element-plus/theme-chalk/src/message.scss'
@ -124,6 +124,8 @@ import CooperModify from '@/management/components/CooperModify/ModifyDialog.vue'
import { CODE_MAP } from '@/management/api/base' import { CODE_MAP } from '@/management/api/base'
import { QOP_MAP } from '@/management/utils/constant.ts' import { QOP_MAP } from '@/management/utils/constant.ts'
import { deleteSurvey } from '@/management/api/survey' import { deleteSurvey } from '@/management/api/survey'
import { useWorkSpaceStore } from '@/management/stores/workSpace'
import { useSurveyListStore } from '@/management/stores/surveyList'
import ModifyDialog from './ModifyDialog.vue' import ModifyDialog from './ModifyDialog.vue'
import TagModule from './TagModule.vue' import TagModule from './TagModule.vue'
import StateModule from './StateModule.vue' import StateModule from './StateModule.vue'
@ -141,7 +143,9 @@ import {
buttonOptionsDict buttonOptionsDict
} from '@/management/config/listConfig' } from '@/management/config/listConfig'
const store = useStore() const surveyListStore = useSurveyListStore()
const workSpaceStore = useWorkSpaceStore()
const { workSpaceId } = storeToRefs(workSpaceStore)
const router = useRouter() const router = useRouter()
const props = defineProps({ const props = defineProps({
loading: { loading: {
@ -162,17 +166,9 @@ const fields = ['type', 'title', 'remark', 'owner', 'state', 'createDate', 'upda
const showModify = ref(false) const showModify = ref(false)
const modifyType = ref('') const modifyType = ref('')
const questionInfo = ref({}) const questionInfo = ref({})
const currentPage = ref(1) const currentPage = ref(1)
const searchVal = computed(() => { const { searchVal, selectValueMap, buttonValueMap } = storeToRefs(surveyListStore)
return store.state.list.searchVal
})
const selectValueMap = computed(() => {
return store.state.list.selectValueMap
})
const buttonValueMap = computed(() => {
return store.state.list.buttonValueMap
})
const currentComponent = computed(() => { const currentComponent = computed(() => {
return (componentName) => { return (componentName) => {
switch (componentName) { switch (componentName) {
@ -247,9 +243,6 @@ const order = computed(() => {
}, []) }, [])
return JSON.stringify(formatOrder) return JSON.stringify(formatOrder)
}) })
const workSpaceId = computed(() => {
return store.state.list.workSpaceId
})
const onRefresh = async () => { const onRefresh = async () => {
const filterString = JSON.stringify( const filterString = JSON.stringify(
@ -298,7 +291,7 @@ const getToolConfig = (row) => {
label: '协作' label: '协作'
} }
] ]
if (!store.state.list.workSpaceId) { if (!workSpaceId.value) {
if (!row.isCollaborated) { if (!row.isCollaborated) {
// //
funcList = funcList.concat(permissionsBtn) funcList = funcList.concat(permissionsBtn)
@ -430,19 +423,18 @@ const onRowClick = (row) => {
}) })
} }
const onSearchText = (e) => { const onSearchText = (e) => {
store.commit('list/setSearchVal', e) searchVal.value = e
currentPage.value = 1 currentPage.value = 1
onRefresh() onRefresh()
} }
const onSelectChange = (selectKey, selectValue) => { const onSelectChange = (selectKey, selectValue) => {
store.commit('list/changeSelectValueMap', { key: selectKey, value: selectValue }) surveyListStore.changeSelectValueMap(selectKey, selectValue)
// selectValueMap.value[selectKey] = selectValue
currentPage.value = 1 currentPage.value = 1
onRefresh() onRefresh()
} }
const onButtonChange = (effectKey, effectValue) => { const onButtonChange = (effectKey, effectValue) => {
store.commit('list/resetButtonValueMap') surveyListStore.resetButtonValueMap()
store.commit('list/changeButtonValueMap', { key: effectKey, value: effectValue }) surveyListStore.changeButtonValueMap(effectKey, effectValue)
onRefresh() onRefresh()
} }
@ -464,16 +456,20 @@ const onCooperClose = () => {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 20px; margin-bottom: 20px;
.select { .select {
display: flex; display: flex;
} }
.search { .search {
display: flex; display: flex;
} }
} }
.list-wrapper { .list-wrapper {
padding: 10px 20px; padding: 10px 20px;
background: #fff; background: #fff;
.list-table { .list-table {
min-height: 620px; min-height: 620px;
} }
@ -481,11 +477,13 @@ const onCooperClose = () => {
.list-pagination { .list-pagination {
margin-top: 20px; margin-top: 20px;
:deep(.el-pagination) { :deep(.el-pagination) {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
} }
} }
:deep(.el-table__header) { :deep(.el-table__header) {
.tableview-header .el-table__cell { .tableview-header .el-table__cell {
.cell { .cell {
@ -495,21 +493,26 @@ const onCooperClose = () => {
} }
} }
} }
:deep(.tableview-row) { :deep(.tableview-row) {
.tableview-cell { .tableview-cell {
padding: 5px 0; padding: 5px 0;
&.link { &.link {
cursor: pointer; cursor: pointer;
} }
.cell .cell-span { .cell .cell-span {
font-size: 14px; font-size: 14px;
} }
} }
} }
} }
.el-select-dropdown__wrap { .el-select-dropdown__wrap {
background: #eee; background: #eee;
} }
.el-select-dropdown__item.hover { .el-select-dropdown__item.hover {
background: #fff; background: #fff;
} }

View File

@ -76,7 +76,6 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useStore } from 'vuex'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import 'element-plus/theme-chalk/src/message-box.scss' import 'element-plus/theme-chalk/src/message-box.scss'
import { get, map } from 'lodash-es' import { get, map } from 'lodash-es'
@ -90,10 +89,10 @@ import TextSearch from '@/management/pages/list/components/TextSearch.vue'
import EmptyIndex from '@/management/components/EmptyIndex.vue' import EmptyIndex from '@/management/components/EmptyIndex.vue'
import ToolBar from './ToolBar.vue' import ToolBar from './ToolBar.vue'
import { UserRole } from '@/management/utils/types/workSpace' import { UserRole } from '@/management/utils/types/workSpace'
import { useWorkSpaceStore } from '@/management/stores/workSpace'
const showSpaceModify = ref(false) const showSpaceModify = ref(false)
const modifyType = ref('edit') const modifyType = ref('edit')
const store = useStore()
const props = defineProps({ const props = defineProps({
loading: { loading: {
type: Boolean, type: Boolean,
@ -109,15 +108,17 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(['refresh']) const emit = defineEmits(['refresh'])
const workSpaceStore = useWorkSpaceStore()
const fields = ['name', 'surveyTotal', 'memberTotal', 'owner', 'createDate'] const fields = ['name', 'surveyTotal', 'memberTotal', 'owner', 'createDate']
const fieldList = computed(() => { const fieldList = computed(() => {
return map(fields, (f) => { return map(fields, (f) => {
return get(spaceListConfig, f, null) return get(spaceListConfig, f, null)
}) })
}) })
const isAdmin = (id: string) => { const isAdmin = (id: string) => {
return ( return (
store.state.list.teamSpaceList.find((item: any) => item._id === id)?.currentUserRole === workSpaceStore.workSpaceList.find((item: any) => item._id === id)?.currentUserRole ===
UserRole.Admin UserRole.Admin
) )
} }
@ -152,7 +153,7 @@ const getTools = (data: any) => {
} }
const handleModify = async (id: string) => { const handleModify = async (id: string) => {
await store.dispatch('list/getSpaceDetail', id) await workSpaceStore.getSpaceDetail(id)
modifyType.value = 'edit' modifyType.value = 'edit'
showSpaceModify.value = true showSpaceModify.value = true
} }
@ -167,9 +168,8 @@ const handleDelete = (id: string) => {
} }
) )
.then(async () => { .then(async () => {
await store.dispatch('list/deleteSpace', id) await workSpaceStore.deleteSpace(id)
await store.dispatch('list/getSpaceList') await workSpaceStore.getSpaceList()
curPage.value = 1
}) })
.catch(() => {}) .catch(() => {})
} }
@ -184,9 +184,9 @@ const handleClick = (key: string, data: any) => {
const onCloseModify = () => { const onCloseModify = () => {
showSpaceModify.value = false showSpaceModify.value = false
store.dispatch('list/getSpaceList') workSpaceStore.getSpaceList()
curPage.value = 1
} }
defineExpose({ onCloseModify }) defineExpose({ onCloseModify })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -44,17 +44,17 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, shallowRef, onMounted } from 'vue' import { computed, ref, shallowRef, onMounted } from 'vue'
import { useStore } from 'vuex'
import { pick as _pick } from 'lodash-es' import { pick as _pick } from 'lodash-es'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss' import 'element-plus/theme-chalk/src/message.scss'
import { QOP_MAP } from '@/management/utils/constant' import { QOP_MAP } from '@/management/utils/constant'
import { type IMember, type IWorkspace, UserRole } from '@/management/utils/types/workSpace' import { type IMember, type IWorkspace, UserRole } from '@/management/utils/types/workSpace'
import { useWorkSpaceStore } from '@/management/stores/workSpace'
import MemberSelect from '@/management/components/CooperModify/MemberSelect.vue' import MemberSelect from '@/management/components/CooperModify/MemberSelect.vue'
const store = useStore() const workSpaceStore = useWorkSpaceStore()
const emit = defineEmits(['on-close-codify', 'onFocus', 'change', 'blur']) const emit = defineEmits(['on-close-codify', 'onFocus', 'change', 'blur'])
const props = defineProps({ const props = defineProps({
type: String, type: String,
@ -66,7 +66,8 @@ const ruleForm = shallowRef<any>(null)
const formTitle = computed(() => { const formTitle = computed(() => {
return props.type === QOP_MAP.ADD ? '创建团队空间' : '管理团队空间' return props.type === QOP_MAP.ADD ? '创建团队空间' : '管理团队空间'
}) })
const formModel = ref<IWorkspace>({ const formModel = ref<Required<IWorkspace>>({
_id: '',
name: '', name: '',
description: '', description: '',
members: [] as IMember[] members: [] as IMember[]
@ -88,28 +89,29 @@ const rules = {
] ]
} }
const spaceDetail = computed(() => { const spaceDetail = computed(() => {
return store.state.list.spaceDetail return workSpaceStore.spaceDetail
}) })
const formDisabled = computed(() => { const formDisabled = computed(() => {
return spaceDetail.value?._id return spaceDetail.value?._id
? store.state.list.teamSpaceList.find((item: any) => item._id === spaceDetail.value._id) ? workSpaceStore.workSpaceList.find((item: any) => item._id === spaceDetail.value?._id)
.currentUserRole !== UserRole.Admin ?.currentUserRole !== UserRole.Admin
: false : false
}) })
onMounted(() => { onMounted(() => {
if (props.type === QOP_MAP.EDIT) { if (props.type === QOP_MAP.EDIT) {
formModel.value = _pick(spaceDetail.value, ['_id', 'name', 'description', 'members']) formModel.value = _pick(spaceDetail.value as any, ['_id', 'name', 'description', 'members'])
} }
}) })
const onClose = () => { const onClose = () => {
formModel.value = { formModel.value = {
_id: '',
name: '', name: '',
description: '', description: '',
members: [] as IMember[] members: [] as IMember[]
} }
// //
store.commit('list/setSpaceDetail', null) workSpaceStore.setSpaceDetail(null)
emit('on-close-codify') emit('on-close-codify')
} }
@ -144,10 +146,10 @@ const handleMembersChange = (val: IMember[]) => {
formModel.value.members = val formModel.value.members = val
} }
const handleUpdate = async () => { const handleUpdate = async () => {
await store.dispatch('list/updateSpace', formModel.value) await workSpaceStore.updateSpace(formModel.value)
} }
const handleAdd = async () => { const handleAdd = async () => {
await store.dispatch('list/addSpace', formModel.value) await workSpaceStore.addSpace(formModel.value)
} }
</script> </script>

View File

@ -56,8 +56,8 @@
ref="spaceListRef" ref="spaceListRef"
@refresh="fetchSpaceList" @refresh="fetchSpaceList"
:loading="spaceLoading" :loading="spaceLoading"
:data="spaceList" :data="workSpaceList"
:total="spaceTotal" :total="workSpaceListTotal"
v-if="spaceType === SpaceType.Group" v-if="spaceType === SpaceType.Group"
></SpaceList> ></SpaceList>
</div> </div>
@ -73,83 +73,66 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import BaseList from './components/BaseList.vue' import BaseList from './components/BaseList.vue'
import SpaceList from './components/SpaceList.vue' import SpaceList from './components/SpaceList.vue'
import SliderBar from './components/SliderBar.vue' import SliderBar from './components/SliderBar.vue'
import SpaceModify from './components/SpaceModify.vue' import SpaceModify from './components/SpaceModify.vue'
import { SpaceType } from '@/management/utils/types/workSpace' import { SpaceType } from '@/management/utils/types/workSpace'
import { useUserStore } from '@/management/stores/user'
import { useWorkSpaceStore } from '@/management/stores/workSpace'
import { useSurveyListStore } from '@/management/stores/surveyList'
const store = useStore() const userStore = useUserStore()
const workSpaceStore = useWorkSpaceStore()
const surveyListStore = useSurveyListStore()
const { surveyList, surveyTotal } = storeToRefs(surveyListStore)
const { spaceMenus, workSpaceId, spaceType, workSpaceList, workSpaceListTotal } =
storeToRefs(workSpaceStore)
const router = useRouter() const router = useRouter()
const userInfo = computed(() => { const userInfo = computed(() => {
return store.state.user.userInfo return userStore.userInfo
}) })
const loading = ref(false) const loading = ref(false)
const surveyList = computed(() => {
return store.state.list.surveyList
})
const surveyTotal = computed(() => {
return store.state.list.surveyTotal
})
const spaceListRef = ref<any>(null) const spaceListRef = ref<any>(null)
const spaceLoading = ref(false) const spaceLoading = ref(false)
const spaceList = computed(() => {
return store.state.list.teamSpaceList
})
const spaceTotal = computed(() => {
return store.state.list.teamSpaceListTotal
})
const fetchSpaceList = async (params?: any) => {
spaceLoading.value = true
store.commit('list/changeWorkSpace', '')
await store.dispatch('list/getSpaceList', params)
spaceLoading.value = false
}
const activeIndex = ref('1') const activeIndex = ref('1')
const spaceMenus = computed(() => { const fetchSpaceList = async (params?: any) => {
return store.state.list.spaceMenus spaceLoading.value = true
}) workSpaceStore.changeWorkSpace('')
const workSpaceId = computed(() => { workSpaceStore.getSpaceList(params)
return store.state.list.workSpaceId spaceLoading.value = false
}) }
const spaceType = computed(() => {
return store.state.list.spaceType
})
const handleSpaceSelect = (id: SpaceType | string) => { const handleSpaceSelect = (id: SpaceType | string) => {
if (id === store.state.list.spaceType || id === store.state.list.workSpaceId) { if (id === spaceType.value || id === workSpaceId.value) {
return void 0 return void 0
} }
const changeSpaceType = (type: SpaceType) => {
store.commit('list/changeSpaceType', type)
}
const changeWorkSpace = (id: string) => {
store.commit('list/changeWorkSpace', id)
}
switch (id) { switch (id) {
case SpaceType.Personal: case SpaceType.Personal:
changeSpaceType(SpaceType.Personal) workSpaceStore.changeSpaceType(SpaceType.Personal)
changeWorkSpace('') workSpaceStore.changeWorkSpace('')
break break
case SpaceType.Group: case SpaceType.Group:
changeSpaceType(SpaceType.Group) workSpaceStore.changeSpaceType(SpaceType.Group)
changeWorkSpace('') workSpaceStore.changeWorkSpace('')
fetchSpaceList() fetchSpaceList()
break break
default: default:
changeSpaceType(SpaceType.Teamwork) workSpaceStore.changeSpaceType(SpaceType.Teamwork)
changeWorkSpace(id) workSpaceStore.changeWorkSpace(id)
break break
} }
fetchSurveyList() fetchSurveyList()
} }
onMounted(() => {
fetchSpaceList()
fetchSurveyList()
})
const fetchSurveyList = async (params?: any) => { const fetchSurveyList = async (params?: any) => {
if (!params) { if (!params) {
params = { params = {
@ -161,19 +144,25 @@ const fetchSurveyList = async (params?: any) => {
params.workspaceId = workSpaceId.value params.workspaceId = workSpaceId.value
} }
loading.value = true loading.value = true
await store.dispatch('list/getSurveyList', params) await surveyListStore.getSurveyList(params)
loading.value = false loading.value = false
} }
onMounted(() => {
fetchSpaceList()
fetchSurveyList()
})
const modifyType = ref('add') const modifyType = ref('add')
const showSpaceModify = ref(false) const showSpaceModify = ref(false)
// //
const currentTeamSpace = computed(() => { const currentTeamSpace = computed(() => {
return store.state.list.teamSpaceList.find((item: any) => item._id === workSpaceId.value) return workSpaceList.value.find((item: any) => item._id === workSpaceId.value)
}) })
const onSetGroup = async () => { const onSetGroup = async () => {
await store.dispatch('list/getSpaceDetail', workSpaceId.value) await workSpaceStore.getSpaceDetail(workSpaceId.value)
modifyType.value = 'edit' modifyType.value = 'edit'
showSpaceModify.value = true showSpaceModify.value = true
} }
@ -192,7 +181,7 @@ const onCreate = () => {
router.push('/create') router.push('/create')
} }
const handleLogout = () => { const handleLogout = () => {
store.dispatch('user/logout') userStore.logout()
router.replace({ name: 'login' }) router.replace({ name: 'login' })
} }
</script> </script>

View File

@ -57,7 +57,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, reactive } from 'vue' import { onMounted, ref, reactive } from 'vue'
import { useStore } from 'vuex'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
@ -66,8 +65,8 @@ import 'element-plus/theme-chalk/src/message.scss'
import { login, register } from '@/management/api/auth' import { login, register } from '@/management/api/auth'
import { refreshCaptcha as refreshCaptchaApi } from '@/management/api/captcha' import { refreshCaptcha as refreshCaptchaApi } from '@/management/api/captcha'
import { CODE_MAP } from '@/management/api/base' import { CODE_MAP } from '@/management/api/base'
import { useUserStore } from '@/management/stores/user'
const store = useStore()
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
@ -150,7 +149,8 @@ const submitForm = (type: 'login' | 'register') => {
ElMessage.error(res.errmsg) ElMessage.error(res.errmsg)
throw new Error('登录/注册失败' + res.errmsg) throw new Error('登录/注册失败' + res.errmsg)
} }
store.dispatch('user/login', { const userStore = useUserStore()
userStore.login({
username: res.data.username, username: res.data.username,
token: res.data.token token: res.data.token
}) })

View File

@ -25,8 +25,8 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted } from 'vue' import { computed, onMounted, toRef } from 'vue'
import { useStore } from 'vuex' import { useEditStore } from '@/management/stores/edit'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { get as _get } from 'lodash-es' import { get as _get } from 'lodash-es'
@ -44,14 +44,15 @@ const defaultConfig = {
img: '/imgs/icons/unpublished.webp' img: '/imgs/icons/unpublished.webp'
} }
const store = useStore() const editStore = useEditStore()
const metaData = computed(() => _get(store.state, 'edit.schema.metaData')) const { schema, init, setSurveyId } = editStore
const metaData = toRef(schema, 'metaData')
const curStatus = computed(() => _get(metaData.value, 'curStatus.status', 'new')) const curStatus = computed(() => _get(metaData.value, 'curStatus.status', 'new'))
const mainChannel = computed(() => { const mainChannel = computed(() => {
let fullUrl = '' let fullUrl = ''
if (metaData.value) { if (metaData.value) {
fullUrl = `${location.origin}/render/${metaData.value.surveyPath}?t=${Date.now()}` fullUrl = `${location.origin}/render/${(metaData.value as any).surveyPath}?t=${Date.now()}`
} }
return { fullUrl } return { fullUrl }
@ -60,10 +61,10 @@ const mainChannel = computed(() => {
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
onMounted(async () => { onMounted(async () => {
store.commit('edit/setSurveyId', route.params.id) setSurveyId(route.params.id as string)
try { try {
await store.dispatch('edit/init') await init()
} catch (err: any) { } catch (err: any) {
ElMessage.error(err.message) ElMessage.error(err.message)
setTimeout(() => { setTimeout(() => {

View File

@ -5,11 +5,12 @@ import {
type NavigationGuardNext type NavigationGuardNext
} from 'vue-router' } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router' import type { RouteRecordRaw } from 'vue-router'
import { useStore, type Store } from 'vuex'
import { SurveyPermissions } from '@/management/utils/types/workSpace' import { SurveyPermissions } from '@/management/utils/types/workSpace'
import { analysisTypeMap } from '@/management/config/analysisConfig' import { analysisTypeMap } from '@/management/config/analysisConfig'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss' import 'element-plus/theme-chalk/src/message.scss'
import { useUserStore } from '@/management/stores/user'
import { useEditStore } from '@/management/stores/edit'
const routes: RouteRecordRaw[] = [ const routes: RouteRecordRaw[] = [
{ {
@ -161,10 +162,10 @@ const router = createRouter({
}) })
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
const store = useStore() const userStore = useUserStore()
// 初始化用户信息 // 初始化用户信息
if (!store.state.user?.initialized) { if (!userStore?.initialized) {
await store.dispatch('user/init') await userStore.init()
} }
// 更新页面标题 // 更新页面标题
if (to.meta.title) { if (to.meta.title) {
@ -172,7 +173,7 @@ router.beforeEach(async (to, from, next) => {
} }
if (to.meta.needLogin) { if (to.meta.needLogin) {
await handleLoginGuard(to, from, next, store) await handleLoginGuard(to, from, next)
} else { } else {
next() next()
} }
@ -181,11 +182,11 @@ router.beforeEach(async (to, from, next) => {
async function handleLoginGuard( async function handleLoginGuard(
to: RouteLocationNormalized, to: RouteLocationNormalized,
from: RouteLocationNormalized, from: RouteLocationNormalized,
next: NavigationGuardNext, next: NavigationGuardNext
store: Store<any>
) { ) {
if (store.state.user?.hasLogined) { const userStore = useUserStore()
await handlePermissionsGuard(to, from, next, store) if (userStore?.hasLogined) {
await handlePermissionsGuard(to, from, next)
} else { } else {
next({ next({
name: 'login', name: 'login',
@ -197,9 +198,9 @@ async function handleLoginGuard(
async function handlePermissionsGuard( async function handlePermissionsGuard(
to: RouteLocationNormalized, to: RouteLocationNormalized,
from: RouteLocationNormalized, from: RouteLocationNormalized,
next: NavigationGuardNext, next: NavigationGuardNext
store: Store<any>
) { ) {
const editStore = useEditStore()
const currSurveyId = to?.params?.id || '' const currSurveyId = to?.params?.id || ''
const prevSurveyId = from?.params?.id || '' const prevSurveyId = from?.params?.id || ''
// 如果跳转页面不存在surveyId 或者不需要页面权限,则直接跳转 // 如果跳转页面不存在surveyId 或者不需要页面权限,则直接跳转
@ -208,8 +209,8 @@ async function handlePermissionsGuard(
} else { } else {
// 如果跳转编辑页面且跳转页面和上一页的surveyId不同判断是否有对应页面权限 // 如果跳转编辑页面且跳转页面和上一页的surveyId不同判断是否有对应页面权限
if (currSurveyId !== prevSurveyId) { if (currSurveyId !== prevSurveyId) {
await store.dispatch('fetchCooperPermissions', currSurveyId) await editStore.fetchCooperPermissions(currSurveyId as string)
if (hasRequiredPermissions(to.meta.permissions as string[], store.state.cooperPermissions)) { if (hasRequiredPermissions(to.meta.permissions as string[], editStore.cooperPermissions)) {
next() next()
} else { } else {
ElMessage.warning('您没有该问卷的相关协作权限') ElMessage.warning('您没有该问卷的相关协作权限')

View File

@ -1,21 +0,0 @@
import { getBannerData } from '@/management/api/skin.js'
import { getCollaboratorPermissions } from '@/management/api/space.ts'
import { CODE_MAP } from '../api/base'
export default {
async getBannerData({ state, commit }) {
if (state.bannerList && state.bannerList.length > 0) {
return
}
const res = await getBannerData()
if (res.code === 200) {
commit('setBannerList', res.data)
}
},
async fetchCooperPermissions({ commit }, id) {
const res = await getCollaboratorPermissions(id)
if (res.code === CODE_MAP.SUCCESS) {
commit('setCooperPermissions', res.data.permissions)
}
}
}

View File

@ -1,72 +0,0 @@
import { getNewField } from '@/management/utils'
import { cloneDeep as _cloneDeep, get as _get } from 'lodash-es'
import { getSurveyById } from '@/management/api/survey'
export default {
async init({ state, dispatch }) {
const metaData = _get(state, 'schema.metaData')
if (!metaData || metaData._id !== state.surveyId) {
await dispatch('getSchemaFromRemote')
}
dispatch('resetState')
},
async getSchemaFromRemote({ commit, state }) {
const res = await getSurveyById(state.surveyId)
if (res.code === 200) {
const metaData = res.data.surveyMetaRes
document.title = metaData.title
const {
bannerConf,
bottomConf,
skinConf,
baseConf,
submitConf,
dataConf,
logicConf = {}
} = res.data.surveyConfRes.code
commit('initSchema', {
metaData,
codeData: {
bannerConf,
bottomConf,
skinConf,
baseConf,
submitConf,
questionDataList: dataConf.dataList,
logicConf
}
})
} else {
throw new Error(res.errmsg || '问卷不存在')
}
},
resetState({ commit }) {
commit('setCurrentEditOne', null)
commit('changeStatusPreview', { type: 'Success' })
},
// 复制题目到当前题目后
copyQuestion({ commit, state }, { index }) {
const newQuestion = _cloneDeep(state.schema.questionDataList[index])
newQuestion.field = getNewField(state.schema.questionDataList.map((item) => item.field))
commit('addQuestion', { question: newQuestion, index })
},
addQuestion({ commit }, { question, index }) {
commit('addQuestion', { question, index })
commit('updateSchemaUpdateTime', Date.now())
},
deleteQuestion({ commit }, { index }) {
commit('deleteQuestion', { index })
commit('updateSchemaUpdateTime', Date.now())
},
moveQuestion({ commit }, { index, range }) {
commit('moveQuestion', { index, range })
commit('updateSchemaUpdateTime', Date.now())
},
changeSchema({ commit }, { key, value }) {
commit('changeSchema', { key, value })
commit('updateSchemaUpdateTime', Date.now())
},
changeThemePreset({ commit }, presets) {
commit('changeThemePreset', presets)
}
}

View File

@ -1,71 +0,0 @@
import submitFormConfig from '@/management/pages/edit/setterConfig/submitConfig'
import questionLoader from '@/materials/questions/questionLoader'
const innerMetaConfig = {
submit: {
title: '提交配置',
formConfig: submitFormConfig
}
}
export default {
moduleConfig(state) {
const currentEditOne = state.currentEditOne
if (currentEditOne === null) {
return null
}
if (currentEditOne === 'banner' || currentEditOne === 'mainTitle') {
return state?.schema?.bannerConf
} else if (currentEditOne === 'submit') {
return state?.schema?.submitConf
} else if (currentEditOne === 'logo') {
return state?.schema?.bottomConf
} else if (!Number.isNaN(currentEditOne)) {
return state?.schema?.questionDataList?.[currentEditOne]
} else {
return null
}
},
formConfigList(state, getters) {
const currentEditOne = state.currentEditOne
if (currentEditOne === null) {
return null
}
return getters?.currentEditMeta?.formConfig || []
},
currentEditMeta(state) {
const currentEditOne = state.currentEditOne
if (currentEditOne === null) {
return null
} else if (innerMetaConfig[currentEditOne]) {
return innerMetaConfig[currentEditOne]
} else {
const questionType = state.schema?.questionDataList?.[currentEditOne]?.type
return questionLoader.getMeta(questionType)
}
},
currentEditKey(state) {
const currentEditOne = state.currentEditOne
if (currentEditOne === null) {
return null
}
let key = ''
switch (currentEditOne) {
case 'banner':
case 'mainTitle':
key = 'bannerConf'
break
case 'submit':
key = 'submitConf'
break
case 'logo':
key = 'bottomConf'
break
default:
key = `questionDataList.${currentEditOne}`
}
return key
}
}

View File

@ -1,12 +0,0 @@
import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
export default {
namespaced: true,
state,
actions,
getters,
mutations
}

View File

@ -1,68 +0,0 @@
import { set as _set, merge as _merge } from 'lodash-es'
export default {
setCurrentEditOne(state, data) {
state.currentEditOne = data
},
changeStatusPreview(state, { type }) {
state.currentEditStatus = type
},
updateSchemaUpdateTime(state) {
state.schemaUpdateTime = Date.now()
},
initSchema(state, { metaData, codeData }) {
state.schema.metaData = metaData
state.schema.bannerConf = _merge({}, state.schema.bannerConf, codeData.bannerConf)
state.schema.bottomConf = _merge({}, state.schema.bottomConf, codeData.bottomConf)
state.schema.skinConf = _merge({}, state.schema.skinConf, codeData.skinConf)
state.schema.baseConf = _merge({}, state.schema.baseConf, codeData.baseConf)
state.schema.submitConf = _merge({}, state.schema.submitConf, codeData.submitConf)
state.schema.questionDataList = codeData.questionDataList || []
state.schema.logicConf = codeData.logicConf
},
setSurveyId(state, data) {
state.surveyId = data
},
addQuestion(state, { question, index }) {
state.schema.questionDataList.splice(index, 0, question)
},
deleteQuestion(state, { index }) {
state.schema.questionDataList.splice(index, 1)
},
moveQuestion(state, { index, range }) {
let start, end
if (range < 0) {
// 向上移动
start = index + range
end = index
} else if (range > 0) {
// 向下移动
start = index + 1
end = index + range + 1
} else {
// 无变化
return
}
const currentData = state.schema.questionDataList[index]
// 新位置和老位置之间所有的题目
const comparedList = state.schema.questionDataList.slice(start, end)
if (range < 0) {
// 向上移动
state.schema.questionDataList.splice(index + range, 1 - range, currentData, ...comparedList)
} else if (range > 0) {
// 向下移动
state.schema.questionDataList.splice(index, range + 1, ...comparedList, currentData)
}
},
changeSchema(state, { key, value }) {
_set(state.schema, key, value)
},
changeThemePreset(state, presets) {
Object.keys(presets).forEach((key) => {
_set(state.schema, key, presets[key])
})
},
setQuestionDataList(state, data) {
state.schema.questionDataList = data
}
}

View File

@ -1,60 +0,0 @@
export default {
currentEditOne: null,
currentEditStatus: 'Success',
schemaUpdateTime: Date.now(),
surveyId: '', // url上取的surveyId
schema: {
metaData: null,
bannerConf: {
titleConfig: {
mainTitle: '<h3 style="text-align: center">欢迎填写问卷</h3>',
subTitle: `<p>为了给您提供更好的服务,希望您能抽出几分钟时间,将您的感受和建议告诉我们,<span style="color: rgb(204, 0, 0)">期待您的参与!</span></p>`,
applyTitle: ''
},
bannerConfig: {
bgImage: '',
bgImageAllowJump: false,
bgImageJumpLink: '',
videoLink: '',
postImg: ''
}
},
bottomConf: {
logoImage: '',
logoImageWidth: '28%'
},
skinConf: {
backgroundConf: {
color: '#fff'
},
themeConf: {
color: '#ffa600'
},
contentConf: {
opacity: 100
}
},
baseConf: {
begTime: '',
endTime: '',
language: 'chinese',
showVoteProcess: 'allow',
tLimit: 0,
answerBegTime: '',
answerEndTime: '',
answerLimitTime: 0
},
submitConf: {
submitTitle: '',
msgContent: {},
confirmAgain: {
is_again: true
},
link: ''
},
questionDataList: [],
logicConf: {
showLogicConf: []
}
}
}

View File

@ -1,19 +0,0 @@
import { createStore } from 'vuex'
import edit from './edit'
import user from './user'
import list from './list'
import actions from './actions'
import mutations from './mutations'
import state from './state'
export default createStore({
state,
getters: {},
mutations,
actions,
modules: {
edit,
user,
list
}
})

View File

@ -1,263 +0,0 @@
import {
createSpace,
getSpaceList,
getSpaceDetail,
updateSpace,
deleteSpace
} from '@/management/api/space'
import { CODE_MAP } from '@/management/api/base'
import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss'
import { getSurveyList as surveyList } from '@/management/api/survey'
import { set } from 'lodash-es'
import { SpaceType } from '@/management/utils/types/workSpace'
export default {
namespaced: true,
state: {
// 空间管理
spaceMenus: [
{
icon: 'icon-wodekongjian',
name: '我的空间',
id: SpaceType.Personal
},
{
icon: 'icon-tuanduikongjian',
name: '团队空间',
id: SpaceType.Group,
children: [
// {
// name: '小桔问卷调研团队',
// id: 'xxxx',
// }
]
}
],
spaceType: SpaceType.Personal,
workSpaceId: '',
spaceDetail: null,
// 团队空间
teamSpaceList: [],
teamSpaceListTotal: 0,
// 列表管理
surveyList: [],
surveyTotal: 0,
searchVal: '',
selectValueMap: {
surveyType: '',
'curStatus.status': ''
},
buttonValueMap: {
'curStatus.date': '',
createDate: -1
}
},
getters: {
listFliter(state) {
return [
{
comparator: '',
condition: [
{
field: 'title',
value: state.searchVal,
comparator: '$regex'
}
]
},
{
comparator: '',
condition: [
{
field: 'curStatus.status',
value: state.selectValueMap['curStatus.status']
}
]
},
{
comparator: '',
condition: [
{
field: 'surveyType',
value: state.selectValueMap.surveyType
}
]
}
]
},
listOrder(state) {
const { buttonValueMap } = state
return Object.entries(buttonValueMap)
.filter(([, effectValue]) => effectValue)
.reduce((prev, item) => {
const [effectKey, effectValue] = item
prev.push({ field: effectKey, value: effectValue })
return prev
}, [])
}
},
mutations: {
updateSpaceMenus(state, teamSpace) {
// 更新空间列表下的团队空间
set(state, 'spaceMenus[1].children', teamSpace)
},
changeSpaceType(state, spaceType) {
state.spaceType = spaceType
},
changeWorkSpace(state, workSpaceId) {
// 切换空间清除筛选条件
this.commit('list/resetSelectValueMap')
this.commit('list/resetButtonValueMap')
this.commit('list/setSearchVal', '')
state.workSpaceId = workSpaceId
},
setSpaceDetail(state, data) {
state.spaceDetail = data
},
setTeamSpaceList(state, data) {
state.teamSpaceList = data
},
setTeamSpaceListTotal(state, teamSpaceListTotal) {
state.teamSpaceListTotal = teamSpaceListTotal
},
setSurveyList(state, list) {
state.surveyList = list
},
setSurveyTotal(state, total) {
state.surveyTotal = total
},
setSearchVal(state, data) {
state.searchVal = data
},
resetSelectValueMap(state) {
state.selectValueMap = {
surveyType: '',
'curStatus.status': ''
}
},
changeSelectValueMap(state, { key, value }) {
state.selectValueMap[key] = value
},
resetButtonValueMap(state) {
state.buttonValueMap = {
'curStatus.date': '',
createDate: -1
}
},
changeButtonValueMap(state, { key, value }) {
state.buttonValueMap[key] = value
}
},
actions: {
async getSpaceList({ commit }, p = { curPage: 1 }) {
try {
const res = await getSpaceList(p)
if (res.code === CODE_MAP.SUCCESS) {
const { list, count } = res.data
const teamSpace = list.map((item) => {
return {
id: item._id,
name: item.name
}
})
commit('setTeamSpaceListTotal', count)
commit('setTeamSpaceList', list)
commit('updateSpaceMenus', teamSpace)
} else {
ElMessage.error('getSpaceList' + res.errmsg)
}
} catch (err) {
ElMessage.error('getSpaceList' + err)
}
},
async addSpace({}, params) {
const res = await createSpace({
name: params.name,
description: params.description,
members: params.members
})
if (res.code === CODE_MAP.SUCCESS) {
ElMessage.success('添加成功')
} else {
ElMessage.error('createSpace code err' + res.errmsg)
}
},
async getSpaceDetail({ state, commit }, id) {
try {
const workspaceId = id || state.workSpaceId
const res = await getSpaceDetail(workspaceId)
if (res.code === CODE_MAP.SUCCESS) {
commit('setSpaceDetail', res.data)
} else {
ElMessage.error('getSpaceList' + res.errmsg)
}
} catch (err) {
ElMessage.error('getSpaceList' + err)
}
},
async updateSpace({}, params) {
const res = await updateSpace({
workspaceId: params._id,
name: params.name,
description: params.description,
members: params.members
})
if (res.code === CODE_MAP.SUCCESS) {
ElMessage.success('更新成功')
} else {
ElMessage.error(res.errmsg)
}
},
async deleteSpace({}, workspaceId) {
try {
const res = await deleteSpace(workspaceId)
if (res.code === CODE_MAP.SUCCESS) {
ElMessage.success('删除成功')
} else {
ElMessage.error(res.errmsg)
}
} catch (err) {
ElMessage.error(err)
}
},
async getSurveyList({ state, getters, commit }, payload) {
const filterString = JSON.stringify(
getters.listFliter.filter((item) => {
return item.condition[0].value
})
)
const orderString = JSON.stringify(getters.listOrder)
try {
let params = {
curPage: payload?.curPage || 1,
pageSize: payload?.pageSize || 10, // 默认一页10条
filter: filterString,
order: orderString,
workspaceId: state.workSpaceId
}
// if(payload?.order) {
// params.order = payload.order
// }
// if(payload.filter) {
// params.filter = payload.filter
// }
// if(payload?.workspaceId) {
// params.workspaceId = payload.workspaceId
// }
const res = await surveyList(params)
if (res.code === CODE_MAP.SUCCESS) {
commit('setSurveyList', res.data.data)
commit('setSurveyTotal', res.data.count)
} else {
ElMessage.error(res.errmsg)
}
} catch (error) {
ElMessage.error('getSurveyList status' + error)
}
}
}
}

View File

@ -1,8 +0,0 @@
export default {
setBannerList(state, data) {
state.bannerList = data
},
setCooperPermissions(state, data) {
state.cooperPermissions = data
}
}

View File

@ -1,5 +0,0 @@
import { SurveyPermissions } from '@/management/utils/types/workSpace'
export default {
bannerList: [],
cooperPermissions: Object.values(SurveyPermissions)
}

View File

@ -1,64 +0,0 @@
const USER_INFO_KEY = 'surveyUserInfo'
export default {
namespaced: true,
state: {
userInfo: {
token: '',
username: ''
},
hasLogined: false,
loginTime: null,
initialized: false
},
mutations: {
setUserInfo(state, data) {
state.userInfo = data
},
setHsLogined(state, data) {
state.hasLogined = data
},
setLoginTime(state, data) {
state.loginTime = data
},
setInitialized(state, data) {
state.initialized = data
}
},
actions: {
init({ dispatch, commit }) {
const localData = localStorage.getItem(USER_INFO_KEY)
if (localData) {
try {
const { userInfo, loginTime } = JSON.parse(localData)
if (Date.now() - loginTime > 7 * 3600000) {
localStorage.removeItem(USER_INFO_KEY)
} else {
dispatch('login', userInfo)
}
} catch (error) {
console.log(error)
}
}
commit('setInitialized', true)
},
login({ commit }, data) {
const loginTime = Date.now()
commit('setUserInfo', data)
commit('setHsLogined', true)
commit('setLoginTime', loginTime)
localStorage.setItem(
USER_INFO_KEY,
JSON.stringify({
userInfo: data,
loginTime: loginTime
})
)
},
logout({ commit }) {
commit('setUserInfo', null)
commit('setHsLogined', false)
localStorage.removeItem(USER_INFO_KEY)
}
}
}

View File

@ -0,0 +1,373 @@
import { type Ref, ref, reactive, toRef, computed } from 'vue'
import { defineStore } from 'pinia'
import { merge as _merge, cloneDeep as _cloneDeep, set as _set } from 'lodash-es'
import { getSurveyById } from '@/management/api/survey'
import { getNewField } from '@/management/utils'
import submitFormConfig from '@/management/pages/edit/setterConfig/statusConfig'
import questionLoader from '@/materials/questions/questionLoader'
import { SurveyPermissions } from '@/management/utils/types/workSpace'
import { getBannerData } from '@/management/api/skin.js'
import { getCollaboratorPermissions } from '@/management/api/space'
import { CODE_MAP } from '../api/base'
const innerMetaConfig = {
submit: {
title: '提交配置',
formConfig: submitFormConfig
}
}
function useInitializeSchema(surveyId: Ref<string>) {
const schema = reactive({
metaData: null,
bannerConf: {
titleConfig: {
mainTitle: '<h3 style="text-align: center">欢迎填写问卷</h3>',
subTitle: `<p>为了给您提供更好的服务,希望您能抽出几分钟时间,将您的感受和建议告诉我们,<span style="color: rgb(204, 0, 0)">期待您的参与!</span></p>`,
applyTitle: ''
},
bannerConfig: {
bgImage: '',
bgImageAllowJump: false,
bgImageJumpLink: '',
videoLink: '',
postImg: ''
}
},
bottomConf: {
logoImage: '',
logoImageWidth: '28%'
},
skinConf: {
backgroundConf: {
color: '#fff'
},
themeConf: {
color: '#ffa600'
},
contentConf: {
opacity: 100
}
},
baseConf: {
begTime: '',
endTime: '',
language: 'chinese',
showVoteProcess: 'allow',
tLimit: 0,
answerBegTime: '',
answerEndTime: '',
answerLimitTime: 0
},
submitConf: {
submitTitle: '',
msgContent: {},
confirmAgain: {
is_again: true
},
link: ''
},
questionDataList: [],
logicConf: {
showLogicConf: []
}
})
function initSchema({ metaData, codeData }: { metaData: any; codeData: any }) {
schema.metaData = metaData
schema.bannerConf = _merge({}, schema.bannerConf, codeData.bannerConf)
schema.bottomConf = _merge({}, schema.bottomConf, codeData.bottomConf)
schema.skinConf = _merge({}, schema.skinConf, codeData.skinConf)
schema.baseConf = _merge({}, schema.baseConf, codeData.baseConf)
schema.submitConf = _merge({}, schema.submitConf, codeData.submitConf)
schema.questionDataList = codeData.questionDataList || []
schema.logicConf = codeData.logicConf
}
async function getSchemaFromRemote() {
const res: any = await getSurveyById(surveyId.value)
if (res.code === 200) {
const metaData = res.data.surveyMetaRes
document.title = metaData.title
const {
bannerConf,
bottomConf,
skinConf,
baseConf,
submitConf,
dataConf,
logicConf = {}
} = res.data.surveyConfRes.code
initSchema({
metaData,
codeData: {
bannerConf,
bottomConf,
skinConf,
baseConf,
submitConf,
questionDataList: dataConf.dataList,
logicConf
}
})
} else {
throw new Error(res.errmsg || '问卷不存在')
}
}
return {
schema,
initSchema,
getSchemaFromRemote
}
}
function useQuestionDataListOperations(questionDataList: Ref<any[]>, updateTime: () => void) {
function copyQuestion({ index }: { index: number }) {
const newQuestion = _cloneDeep(questionDataList.value[index])
newQuestion.field = getNewField(questionDataList.value.map((item) => item.field))
addQuestion({ question: newQuestion, index })
}
function addQuestion({ question, index }: { question: any; index: number }) {
questionDataList.value.splice(index, 0, question)
updateTime()
}
function deleteQuestion({ index }: { index: number }) {
questionDataList.value.splice(index, 1)
updateTime()
}
function moveQuestion({ index, range }: { index: number; range: number }) {
console.log('move')
let start, end
if (range < 0) {
// 向上移动
start = index + range
end = index
} else if (range > 0) {
// 向下移动
start = index + 1
end = index + range + 1
} else {
// 无变化
return
}
const currentData = questionDataList.value[index]
// 新位置和老位置之间所有的题目
const comparedList = questionDataList.value.slice(start, end)
if (range < 0) {
// 向上移动
questionDataList.value.splice(index + range, 1 - range, currentData, ...comparedList)
} else if (range > 0) {
// 向下移动
questionDataList.value.splice(index, range + 1, ...comparedList, currentData)
}
updateTime()
}
return {
copyQuestion,
addQuestion,
deleteQuestion,
moveQuestion
}
}
function useCurrentEdit({
schema,
questionDataList
}: {
schema: any
questionDataList: Ref<any[]>
}) {
const currentEditOne = ref()
const currentEditStatus = ref('Success')
const currentEditKey = computed(() => {
if (currentEditOne.value === null) {
return null
}
let key = ''
switch (currentEditOne.value) {
case 'banner':
case 'mainTitle':
key = 'bannerConf'
break
case 'submit':
key = 'submitConf'
break
case 'logo':
key = 'bottomConf'
break
default:
key = `questionDataList.${currentEditOne.value}`
}
return key
})
const currentEditMeta = computed(() => {
if (currentEditOne.value === null) {
return null
} else if (innerMetaConfig[currentEditOne.value as keyof typeof innerMetaConfig]) {
return innerMetaConfig[currentEditOne.value as keyof typeof innerMetaConfig]
} else {
const questionType = questionDataList.value?.[currentEditOne.value]?.type
return questionLoader.getMeta(questionType)
}
})
const moduleConfig = computed(() => {
if (currentEditOne.value === null) {
return null
}
if (currentEditOne.value === 'banner' || currentEditOne.value === 'mainTitle') {
return schema?.bannerConf
} else if (currentEditOne.value === 'submit') {
return schema?.submitConf
} else if (currentEditOne.value === 'logo') {
return schema?.bottomConf
} else if (!Number.isNaN(currentEditOne.value)) {
return questionDataList.value?.[currentEditOne.value]
} else {
return null
}
})
const formConfigList = computed(() => {
if (currentEditOne.value === null) {
return null
}
return currentEditMeta.value?.formConfig || []
})
function setCurrentEditOne(data: any) {
currentEditOne.value = data
}
function changeCurrentEditStatus(status: string) {
currentEditStatus.value = status
}
return {
currentEditOne,
currentEditKey,
currentEditStatus,
moduleConfig,
formConfigList,
currentEditMeta,
setCurrentEditOne,
changeCurrentEditStatus
}
}
type IBannerItem = {
name: string
key: string
list: Array<Object>
}
type IBannerList = Record<string, IBannerItem>
export const useEditStore = defineStore('edit', () => {
const surveyId = ref('')
const bannerList: Ref<IBannerList> = ref({})
const cooperPermissions = ref(Object.values(SurveyPermissions))
const schemaUpdateTime = ref(Date.now())
const { schema, initSchema, getSchemaFromRemote } = useInitializeSchema(surveyId)
const questionDataList = toRef(schema, 'questionDataList')
function setQuestionDataList(data: any) {
schema.questionDataList = data
}
function setSurveyId(id: string) {
surveyId.value = id
}
const fetchBannerData = async () => {
const res: any = await getBannerData()
if (res.code === CODE_MAP.SUCCESS) {
bannerList.value = res.data
}
}
const fetchCooperPermissions = async (id: string) => {
const res: any = await getCollaboratorPermissions(id)
if (res.code === CODE_MAP.SUCCESS) {
cooperPermissions.value = res.data.permissions
}
}
const {
currentEditOne,
currentEditKey,
currentEditStatus,
moduleConfig,
formConfigList,
currentEditMeta,
setCurrentEditOne,
changeCurrentEditStatus
} = useCurrentEdit({ schema, questionDataList })
async function init() {
const { metaData } = schema
if (!metaData || (metaData as any)?._id !== surveyId.value) {
getSchemaFromRemote()
}
currentEditOne.value = null
currentEditStatus.value = 'Success'
}
function updateTime() {
schemaUpdateTime.value = Date.now()
}
const { copyQuestion, addQuestion, deleteQuestion, moveQuestion } = useQuestionDataListOperations(
questionDataList,
updateTime
)
function changeSchema({ key, value }: { key: string; value: any }) {
_set(schema, key, value)
updateTime()
}
function changeThemePreset(presets: any) {
Object.keys(presets).forEach((key) => {
_set(schema, key, presets[key])
})
}
return {
surveyId,
setSurveyId,
bannerList,
fetchBannerData,
cooperPermissions,
fetchCooperPermissions,
currentEditOne,
moduleConfig,
formConfigList,
currentEditKey,
currentEditStatus,
currentEditMeta,
setCurrentEditOne,
changeCurrentEditStatus,
schemaUpdateTime,
schema,
questionDataList,
setQuestionDataList,
init,
initSchema,
getSchemaFromRemote,
copyQuestion,
addQuestion,
deleteQuestion,
moveQuestion,
changeSchema,
changeThemePreset
}
})

View File

@ -0,0 +1,170 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss'
import { CODE_MAP } from '@/management/api/base'
import { getSurveyList as getSurveyListReq } from '@/management/api/survey'
import { useWorkSpaceStore } from './workSpace'
function useSearchSurvey() {
const searchVal = ref('')
const selectValueMap = ref<Record<string, any>>({
surveyType: '',
'curStatus.status': ''
})
const buttonValueMap = ref<Record<string, any>>({
'curStatus.date': '',
createDate: -1
})
const listFilter = computed(() => {
return [
{
comparator: '',
condition: [
{
field: 'title',
value: searchVal.value,
comparator: '$regex'
}
]
},
{
comparator: '',
condition: [
{
field: 'curStatus.status',
value: selectValueMap.value['curStatus.status']
}
]
},
{
comparator: '',
condition: [
{
field: 'surveyType',
value: selectValueMap.value.surveyType
}
]
}
]
})
const listOrder = computed(() => {
return Object.entries(buttonValueMap.value)
.filter(([, effectValue]) => effectValue)
.reduce((prev: { field: string; value: string | number }[], item) => {
const [effectKey, effectValue] = item
prev.push({ field: effectKey, value: effectValue })
return prev
}, [])
})
function resetSelectValueMap() {
selectValueMap.value = {
surveyType: '',
'curStatus.status': ''
}
}
function resetButtonValueMap() {
buttonValueMap.value = {
'curStatus.date': '',
createDate: -1
}
}
function changeSelectValueMap(key: string, value: string | number) {
selectValueMap.value[key] = value
}
function changeButtonValueMap(key: string, value: string | number) {
buttonValueMap.value[key] = value
}
function resetSearch() {
searchVal.value = ''
resetSelectValueMap()
resetButtonValueMap()
}
return {
searchVal,
selectValueMap,
buttonValueMap,
listFilter,
listOrder,
resetSearch,
resetSelectValueMap,
resetButtonValueMap,
changeSelectValueMap,
changeButtonValueMap
}
}
export const useSurveyListStore = defineStore('surveyList', () => {
const surveyList = ref([])
const surveyTotal = ref(0)
const {
searchVal,
selectValueMap,
buttonValueMap,
listFilter,
listOrder,
resetSearch,
resetSelectValueMap,
resetButtonValueMap,
changeSelectValueMap,
changeButtonValueMap
} = useSearchSurvey()
const workSpaceStore = useWorkSpaceStore()
async function getSurveyList(payload: { curPage?: number; pageSize?: number }) {
const filterString = JSON.stringify(
listFilter.value.filter((item) => {
return item.condition[0].value
})
)
const orderString = JSON.stringify(listOrder.value)
try {
const params = {
curPage: payload?.curPage || 1,
pageSize: payload?.pageSize || 10, // 默认一页10条
filter: filterString,
order: orderString,
workspaceId: workSpaceStore.workSpaceId
}
const res: any = await getSurveyListReq(params)
if (res.code === CODE_MAP.SUCCESS) {
surveyList.value = res.data.data
surveyTotal.value = res.data.count
} else {
ElMessage.error(res.errmsg)
}
} catch (error) {
ElMessage.error('getSurveyList status' + error)
}
}
return {
surveyList,
surveyTotal,
searchVal,
selectValueMap,
buttonValueMap,
listFilter: listFilter,
listOrder,
resetSearch,
getSurveyList,
resetSelectValueMap,
resetButtonValueMap,
changeSelectValueMap,
changeButtonValueMap
}
})

View File

@ -0,0 +1,54 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
type IUserInfo = {
username: string
token: string
}
const USER_INFO_KEY = 'surveyUserInfo'
export const useUserStore = defineStore('user', () => {
const userInfo = ref<IUserInfo | null>({
username: '',
token: ''
})
const hasLogined = ref(false)
const loginTime = ref<number | null>(null)
const initialized = ref(false)
const init = () => {
const localData = localStorage.getItem(USER_INFO_KEY)
if (localData) {
try {
const { userInfo: info, loginTime: time } = JSON.parse(localData)
if (Date.now() - time > 7 * 3600000) {
localStorage.removeItem(USER_INFO_KEY)
} else {
login(info)
}
} catch (error) {
console.log(error)
}
}
initialized.value = true
}
const login = (data: IUserInfo) => {
userInfo.value = data
hasLogined.value = true
loginTime.value = Date.now()
localStorage.setItem(
USER_INFO_KEY,
JSON.stringify({
userInfo: data,
loginTime: loginTime
})
)
}
const logout = () => {
userInfo.value = null
hasLogined.value = false
localStorage.removeItem(USER_INFO_KEY)
}
return { userInfo, hasLogined, loginTime, initialized, init, login, logout }
})

View File

@ -0,0 +1,149 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'
import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss'
import { CODE_MAP } from '@/management/api/base'
import {
createSpace,
updateSpace as updateSpaceReq,
deleteSpace as deleteSpaceReq,
getSpaceList as getSpaceListReq,
getSpaceDetail as getSpaceDetailReq
} from '@/management/api/space'
import { SpaceType } from '@/management/utils/types/workSpace'
import {
type SpaceDetail,
type SpaceItem,
type IWorkspace
} from '@/management/utils/types/workSpace'
import { useSurveyListStore } from './surveyList'
export const useWorkSpaceStore = defineStore('workSpace', () => {
// list空间
const spaceMenus = ref([
{
icon: 'icon-wodekongjian',
name: '我的空间',
id: SpaceType.Personal
},
{
icon: 'icon-tuanduikongjian',
name: '团队空间',
id: SpaceType.Group,
children: []
}
])
const spaceType = ref(SpaceType.Personal)
const workSpaceId = ref('')
const spaceDetail = ref<SpaceDetail | null>(null)
const workSpaceList = ref<SpaceItem[]>([])
const workSpaceListTotal = ref(0)
const surveyListStore = useSurveyListStore()
async function getSpaceList(params = { curPage: 1 }) {
try {
const res: any = await getSpaceListReq(params)
if (res.code === CODE_MAP.SUCCESS) {
const { list } = res.data
const workSpace = list.map((item: SpaceDetail) => {
return {
id: item._id,
name: item.name
}
})
workSpaceList.value = list
spaceMenus.value[1].children = workSpace
} else {
ElMessage.error('getSpaceList' + res.errmsg)
}
} catch (err) {
ElMessage.error('getSpaceList' + err)
}
}
async function getSpaceDetail(id: string) {
try {
const _id = id || workSpaceId.value
const res: any = await getSpaceDetailReq(_id)
if (res.code === CODE_MAP.SUCCESS) {
spaceDetail.value = res.data
} else {
ElMessage.error('getSpaceList' + res.errmsg)
}
} catch (err) {
ElMessage.error('getSpaceList' + err)
}
}
function changeSpaceType(id: SpaceType) {
spaceType.value = id
}
function changeWorkSpace(id: string) {
workSpaceId.value = id
surveyListStore.resetSearch()
}
async function deleteSpace(id: string) {
try {
const res: any = await deleteSpaceReq(id)
if (res.code === CODE_MAP.SUCCESS) {
ElMessage.success('删除成功')
} else {
ElMessage.error(res.errmsg)
}
} catch (err: any) {
ElMessage.error(err)
}
}
async function updateSpace(params: Required<IWorkspace>) {
const { _id: workspaceId, name, description, members } = params
const res: any = await updateSpaceReq({ workspaceId, name, description, members })
if (res?.code === CODE_MAP.SUCCESS) {
ElMessage.success('更新成功')
} else {
ElMessage.error(res?.errmsg)
}
}
async function addSpace(params: IWorkspace) {
const { name, description, members } = params
const res: any = await createSpace({ name, description, members })
if (res.code === CODE_MAP.SUCCESS) {
ElMessage.success('添加成功')
} else {
ElMessage.error('createSpace code err' + res.errmsg)
}
}
function setSpaceDetail(data: null | SpaceDetail) {
spaceDetail.value = data
}
return {
spaceMenus,
spaceType,
workSpaceId,
spaceDetail,
workSpaceList,
workSpaceListTotal,
getSpaceList,
getSpaceDetail,
changeSpaceType,
changeWorkSpace,
addSpace,
deleteSpace,
updateSpace,
setSpaceDetail
}
})

View File

@ -51,7 +51,7 @@ export const getQuestionByType = (type, fields) => {
} }
newQuestion.field = getNewField(fields) // 动态生成题目id newQuestion.field = getNewField(fields) // 动态生成题目id
if ('options ' in newQuestion) { if ('options' in newQuestion) {
// 动态更新选项的hash-id // 动态更新选项的hash-id
const hashList = [] const hashList = []
for (const option of newQuestion.options) { for (const option of newQuestion.options) {

View File

@ -11,11 +11,12 @@ export interface MenuItem {
} }
export type IWorkspace = { export type IWorkspace = {
id?: string _id?: string
name: string name: string
description: string description: string
members: IMember[] members: IMember[]
} }
export type IMember = { export type IMember = {
userId: string userId: string
username: string username: string
@ -23,6 +24,24 @@ export type IMember = {
_id?: string _id?: string
} }
export interface SpaceDetail {
_id?: string
name: string
currentUserId?: string
description: string
members: IMember[]
}
export type SpaceItem = Required<Omit<SpaceDetail, 'members'>> & {
createDate: string
curStatus: { date: number; status: string }
memberTotal: number
currentUserRole: string
owner: string
ownerId: string
surveyTotal: number
}
export enum SpaceType { export enum SpaceType {
Personal = 'personal', Personal = 'personal',
Group = 'group', Group = 'group',

View File

@ -2,6 +2,7 @@
<el-input <el-input
:placeholder="formConfig.placeholder" :placeholder="formConfig.placeholder"
v-model="modelValue" v-model="modelValue"
:maxlength="formConfig.maxLength"
@blur="handleInputBlur" @blur="handleInputBlur"
></el-input> ></el-input>
</template> </template>

View File

@ -1,33 +0,0 @@
<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>

View File

@ -1,52 +0,0 @@
<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>

View File

@ -2,12 +2,12 @@
<router-view></router-view> <router-view></router-view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, watch } from 'vue' import { watch } from 'vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { get as _get } from 'lodash-es'
const store = useStore() import { useSurveyStore } from './stores/survey'
const skinConf = computed(() => _get(store, 'state.skinConf', {}))
const { skinConf } = storeToRefs(useSurveyStore())
const updateSkinConfig = (value: any) => { const updateSkinConfig = (value: any) => {
const root = document.documentElement const root = document.documentElement

View File

@ -33,7 +33,7 @@ export const getEncryptInfo = () => {
return axios.get('/clientEncrypt/getEncryptInfo') return axios.get('/clientEncrypt/getEncryptInfo')
} }
export const validate = ({ surveyPath,password, whitelist }) => { export const validate = ({ surveyPath, password, whitelist }) => {
return axios.post(`/responseSchema/${surveyPath}/validate`, { return axios.post(`/responseSchema/${surveyPath}/validate`, {
password, password,
whitelist whitelist

View File

@ -13,17 +13,20 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import MaterialGroup from './MaterialGroup.vue' import MaterialGroup from './MaterialGroup.vue'
import { useQuestionStore } from '../stores/question'
import { useSurveyStore } from '../stores/survey'
const store = useStore() const surveyStore = useSurveyStore()
const questionStore = useQuestionStore()
const renderData = computed(() => store.getters?.renderData) const renderData = computed(() => questionStore.renderData)
const rules = computed(() => store.state.rules)
const formValues = computed(() => store.state.formValues) const { rules, formValues } = storeToRefs(surveyStore)
const handleChangeData = (data: any) => { const handleChangeData = (data: any) => {
store.dispatch('changeData', data) surveyStore.changeData(data)
} }
</script> </script>

View File

@ -13,9 +13,10 @@ import QuestionRuleContainer from '../../materials/questions/QuestionRuleContain
import { useVoteMap } from '@/render/hooks/useVoteMap' import { useVoteMap } from '@/render/hooks/useVoteMap'
import { useShowOthers } from '@/render/hooks/useShowOthers' import { useShowOthers } from '@/render/hooks/useShowOthers'
import { useShowInput } from '@/render/hooks/useShowInput' import { useShowInput } from '@/render/hooks/useShowInput'
import store from '@/render/store'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { ruleEngine } from '@/render/hooks/useRuleEngine.js' import { ruleEngine } from '@/render/hooks/useRuleEngine.js'
import { useQuestionStore } from '../stores/question'
import { useSurveyStore } from '../stores/survey'
import { NORMAL_CHOICES, RATES, QUESTION_TYPE } from '@/common/typeEnum.ts' import { NORMAL_CHOICES, RATES, QUESTION_TYPE } from '@/common/typeEnum.ts'
@ -32,9 +33,11 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(['change']) const emit = defineEmits(['change'])
const questionStore = useQuestionStore()
const surveyStore = useSurveyStore()
const formValues = computed(() => { const formValues = computed(() => {
return store.state.formValues return surveyStore.formValues
}) })
const questionConfig = computed(() => { const questionConfig = computed(() => {
let moduleConfig = props.moduleConfig let moduleConfig = props.moduleConfig
@ -96,7 +99,7 @@ watch(
key: field, key: field,
value: value value: value
} }
store.commit('changeFormData', data) surveyStore.changeData(data)
} }
} }
) )
@ -105,7 +108,7 @@ const handleChange = (data) => {
emit('change', data) emit('change', data)
// //
if (props.moduleConfig.type === QUESTION_TYPE.VOTE) { if (props.moduleConfig.type === QUESTION_TYPE.VOTE) {
store.dispatch('updateVoteData', data) questionStore.updateVoteData(data)
} }
} }
</script> </script>

View File

@ -1,43 +1,52 @@
<template> <template>
<el-dialog <el-dialog
v-model="whiteVisible" v-model="whiteVisible"
title="验证" title="验证"
:show-close="false" :show-close="false"
class="verify-white-wrap" class="verify-white-wrap"
width="315" width="315"
:close-on-press-escape="false" :close-on-press-escape="false"
:close-on-click-modal="false" :close-on-click-modal="false"
align-center align-center
>
> <template #header>
<template #header> <div class="verify-white-head">
<div class="verify-white-head"> <div class="verify-white-title">验证</div>
<div class="verify-white-title">验证</div> <div v-if="whitelistTip" class="verify-white-tips">{{ whitelistTip }}</div>
<div v-if="whitelistTip" class="verify-white-tips">{{ whitelistTip }}</div> </div>
</div> </template>
</template> <div class="verify-white-body">
<div class="verify-white-body"> <el-input
<el-input v-if="isPwd" v-model="state.password" class="wd255 mb16" placeholder="请输入6位字符串类型访问密码" /> v-if="isPwd"
<el-input v-if="isValue" v-model="state.value" class="wd255 mb16" :placeholder="placeholder" /> v-model="state.password"
<div class="submit-btn" @click="handleSubmit">验证并开始答题</div> class="wd255 mb16"
</div> placeholder="请输入6位字符串类型访问密码"
</el-dialog> />
<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> </template>
<script setup> <script setup>
import { ref,reactive,computed,watch} from 'vue' import { ref, reactive, computed, watch } from 'vue'
import { validate } from '../api/survey'
import { useStore } from 'vuex'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/src/message.scss' import 'element-plus/theme-chalk/src/message.scss'
import { validate } from '../api/survey'
import { useSurveyStore } from '../stores/survey'
const whiteVisible = ref(false) const whiteVisible = ref(false)
const surveyStore = useSurveyStore()
const store = useStore()
const state = reactive({ const state = reactive({
password: '', password: '',
value: '', value: '',
is_submit:false is_submit: false
}) })
const baseConf = computed(() => store.state.baseConf || {}) const baseConf = computed(() => store.state.baseConf || {})
@ -48,91 +57,91 @@ const whitelistTip = computed(() => baseConf.value.whitelistTip)
const surveyPath = computed(() => store.state?.surveyPath || '') const surveyPath = computed(() => store.state?.surveyPath || '')
const isValue = computed(() => { const isValue = computed(() => {
if(!whitelistType.value) return false if (!whitelistType.value) return false
return whitelistType.value!='ALL' return whitelistType.value != 'ALL'
}) })
const placeholder = computed(() => { const placeholder = computed(() => {
if (whitelistType.value == 'MEMBER') { if (whitelistType.value == 'MEMBER') {
return '请输入用户名' return '请输入用户名'
} }
if(memberType.value == 'MOBILE'){ if (memberType.value == 'MOBILE') {
return '请输入手机号' return '请输入手机号'
} }
if(memberType.value == 'EMAIL'){ if (memberType.value == 'EMAIL') {
return '请输入邮箱' return '请输入邮箱'
} }
return '' return ''
}) })
const handleSubmit = async() => { const handleSubmit = async () => {
if (state.is_submit) return; if (state.is_submit) return
const params = { const params = {
surveyPath:surveyPath.value surveyPath: surveyPath.value
} }
if (isValue.value) { if (isValue.value) {
params.whitelist = state.value params.whitelist = state.value
} }
if(isPwd.value){ if (isPwd.value) {
params.password = state.password params.password = state.password
} }
const res = await validate(params) const res = await validate(params)
if (res.code != 200) { if (res.code != 200) {
ElMessage.error(res.errmsg || '验证失败') ElMessage.error(res.errmsg || '验证失败')
return return
} }
whiteVisible.value = false whiteVisible.value = false
store.commit('setWhiteData',params) surveyStore.setWhiteData(params)
} }
watch(()=>baseConf.value, () => { watch(
if (whiteVisible.value) return () => baseConf.value,
if(isValue.value || isPwd.value){ () => {
whiteVisible.value = true; if (whiteVisible.value) return
} if (isValue.value || isPwd.value) {
}) whiteVisible.value = true
}
}
)
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.verify-white-wrap{ .verify-white-wrap {
.verify-white-body{ .verify-white-body {
padding:0 14px padding: 0 14px;
} }
.verify-white-head{ .verify-white-head {
padding:0 14px; padding: 0 14px;
margin-bottom: 8px; margin-bottom: 8px;
margin-top:2px; margin-top: 2px;
} }
.mb16{ .mb16 {
margin-bottom:16px; margin-bottom: 16px;
} }
.verify-white-tips{ .verify-white-tips {
text-align: center; text-align: center;
margin-top:8px; margin-top: 8px;
font-size: 14px; font-size: 14px;
color: #92949D; color: #92949d;
} }
.verify-white-title{ .verify-white-title {
font-size: 16px; font-size: 16px;
color: #292A36; color: #292a36;
font-weight: 500; font-weight: 500;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
}
} .submit-btn {
.submit-btn{ background: #faa600;
background: #FAA600; border-radius: 2px;
border-radius: 2px; width: 255px;
width:255px; height: 32px;
height:32px; color: #fff;
color:#fff; display: flex;
display: flex; align-items: center;
align-items: center; justify-content: center;
justify-content: center; margin-top: 4px;
margin-top:4px; margin-bottom: 14px;
margin-bottom:14px; }
}
} }
</style> </style>

View File

@ -1,6 +1,7 @@
import store from '../store/index' import { useSurveyStore } from '../stores/survey'
import { computed } from 'vue' import { computed } from 'vue'
export const useProgressBar = () => { export const useProgressBar = () => {
const surveyStore = useSurveyStore()
const isVariableEmpty = (variable) => { const isVariableEmpty = (variable) => {
if (variable === undefined || variable === null) { if (variable === undefined || variable === null) {
return true return true
@ -22,7 +23,7 @@ export const useProgressBar = () => {
fillCount: 0, fillCount: 0,
topicCount: 0 topicCount: 0
} }
const formValues = store.state.formValues const formValues = surveyStore.formValues
for (let key in formValues) { for (let key in formValues) {
if (key.split('_').length > 1) continue if (key.split('_').length > 1) continue

View File

@ -1,8 +1,12 @@
import store from '../store/index' import { useQuestionStore } from '../stores/question'
import { useSurveyStore } from '../stores/survey'
export const useShowInput = (questionKey) => { export const useShowInput = (questionKey) => {
const formValues = store.state.formValues const questionStore = useQuestionStore()
const surveyStore = useSurveyStore()
const formValues = surveyStore.formValues
const questionVal = formValues[questionKey] const questionVal = formValues[questionKey]
let rangeConfig = store.state.questionData[questionKey].rangeConfig let rangeConfig = questionStore.questionData[questionKey].rangeConfig
let othersValue = {} let othersValue = {}
if (rangeConfig && Object.keys(rangeConfig).length > 0) { if (rangeConfig && Object.keys(rangeConfig).length > 0) {
for (let key in rangeConfig) { for (let key in rangeConfig) {
@ -18,7 +22,8 @@ export const useShowInput = (questionKey) => {
key: rangeKey, key: rangeKey,
value: '' value: ''
} }
store.commit('changeFormData', data)
surveyStore.changeData(data)
} }
} }
} }

View File

@ -1,9 +1,13 @@
import store from '../store/index' import { useQuestionStore } from '../stores/question'
import { useSurveyStore } from '../stores/survey'
export const useShowOthers = (questionKey) => { export const useShowOthers = (questionKey) => {
const formValues = store.state.formValues const questionStore = useQuestionStore()
const surveyStore = useSurveyStore()
const formValues = surveyStore.formValues
const questionVal = formValues[questionKey] const questionVal = formValues[questionKey]
let othersValue = {} let othersValue = {}
let options = store.state.questionData[questionKey].options.map((optionItem) => { let options = questionStore.questionData[questionKey].options.map((optionItem) => {
if (optionItem.others) { if (optionItem.others) {
const opKey = `${questionKey}_${optionItem.hash}` const opKey = `${questionKey}_${optionItem.hash}`
othersValue[opKey] = formValues[opKey] othersValue[opKey] = formValues[opKey]
@ -13,7 +17,7 @@ export const useShowOthers = (questionKey) => {
key: opKey, key: opKey,
value: '' value: ''
} }
store.commit('changeFormData', data) surveyStore.changeData(data)
} }
return { return {
...optionItem, ...optionItem,

View File

@ -1,10 +1,12 @@
import store from '../store/index' import { useQuestionStore } from '../stores/question'
export const useVoteMap = (questionKey) => {
let voteTotal = store.state.voteMap?.[questionKey]?.total || 0
const options = store.state.questionData[questionKey].options.map((option) => { export const useVoteMap = (questionKey) => {
const questionStore = useQuestionStore()
let voteTotal = questionStore.voteMap?.[questionKey]?.total || 0
const options = questionStore.questionData[questionKey].options.map((option) => {
const optionHash = option.hash const optionHash = option.hash
const voteCount = store.state.voteMap?.[questionKey]?.[optionHash] || 0 const voteCount = questionStore.voteMap?.[questionKey]?.[optionHash] || 0
return { return {
...option, ...option,

View File

@ -2,15 +2,16 @@ import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import EventBus from './utils/eventbus' import EventBus from './utils/eventbus'
import router from './router' import router from './router'
import store from './store' import { createPinia } from 'pinia'
const app = createApp(App) const app = createApp(App)
const pinia = createPinia()
const $bus = new EventBus() const $bus = new EventBus()
app.provide('$bus', $bus) app.provide('$bus', $bus)
// 挂载到this上 // 挂载到this上
app.config.globalProperties.$bus = $bus app.config.globalProperties.$bus = $bus
app.use(pinia)
app.use(router) app.use(router)
app.use(store)
app.mount('#app') app.mount('#app')

View File

@ -2,35 +2,41 @@
<div class="result-page-wrap"> <div class="result-page-wrap">
<div class="result-page"> <div class="result-page">
<div class="page-content"> <div class="page-content">
<img class="img" :src="errorImageUrl" /> <img class="img" :src="errorImage" />
<div class="msg" v-html="errorMsg"></div> <div class="msg" v-html="errorMsg"></div>
</div> </div>
<LogoIcon :logo-conf="logoConf" :readonly="true" /> <LogoIcon :logo-conf="logoConf" :readonly="true" />
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup>
import { computed } from 'vue' import { computed } from 'vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
// @ts-ignore
import communalLoader from '@materials/communals/communalLoader.js' import communalLoader from '@materials/communals/communalLoader.js'
import { useErrorInfo } from '../stores/errorInfo'
import { useSurveyStore } from '../stores/survey'
const LogoIcon = communalLoader.loadComponent('LogoIcon') const LogoIcon = communalLoader.loadComponent('LogoIcon')
const store = useStore() const surveyStore = useSurveyStore()
const errorStore = useErrorInfo()
const { errorInfo } = storeToRefs(errorStore)
const imageMap = {
overTime: '/imgs/icons/overtime.webp',
default: '/imgs/icons/error.webp'
}
const errorImageUrl = computed(() => { const errorImage = computed(() => {
const errorType = store.state?.errorInfo?.errorType const errorType = errorInfo.value.errorType
const imageMap = {
overTime: '/imgs/icons/overtime.webp',
default: '/imgs/icons/error.webp'
}
return imageMap[errorType as 'overTime'] || imageMap.default return imageMap[errorType] || imageMap.default
}) })
const errorMsg = computed(() => store.state?.errorInfo?.errorMsg || '提交失败') const errorMsg = computed(() => {
const logoConf = computed(() => store.state?.bottomConf || {}) return errorInfo.value.errorMsg || '提交失败'
})
const logoConf = computed(() => surveyStore.bottomConf || {})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.result-page-wrap { .result-page-wrap {

View File

@ -3,17 +3,16 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted } from 'vue' import { onMounted } from 'vue'
import { useStore } from 'vuex'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { getPublishedSurveyInfo, getPreviewSchema } from '../api/survey' import { getPublishedSurveyInfo, getPreviewSchema } from '../api/survey'
import useCommandComponent from '../hooks/useCommandComponent' import useCommandComponent from '../hooks/useCommandComponent'
import { useSurveyStore } from '../stores/survey'
import AlertDialog from '../components/AlertDialog.vue' import AlertDialog from '../components/AlertDialog.vue'
import { initRuleEngine } from '@/render/hooks/useRuleEngine.js' import { initRuleEngine } from '@/render/hooks/useRuleEngine.js'
const store = useStore()
const route = useRoute() const route = useRoute()
const surveyStore = useSurveyStore()
const loadData = (res: any, surveyPath: string) => { const loadData = (res: any, surveyPath: string) => {
if (res.code === 200) { if (res.code === 200) {
const data = res.data const data = res.data
@ -30,8 +29,8 @@ const loadData = (res: any, surveyPath: string) => {
document.title = data.title document.title = data.title
store.commit('setSurveyPath', surveyPath) surveyStore.setSurveyPath(surveyPath)
store.dispatch('init', questionData) surveyStore.initSurvey(questionData)
initRuleEngine(logicConf?.showLogicConf) initRuleEngine(logicConf?.showLogicConf)
} else { } else {
throw new Error(res.errmsg) throw new Error(res.errmsg)
@ -40,7 +39,7 @@ const loadData = (res: any, surveyPath: string) => {
onMounted(() => { onMounted(() => {
const surveyId = route.params.surveyId const surveyId = route.params.surveyId
console.log({ surveyId }) console.log({ surveyId })
store.commit('setSurveyPath', surveyId) surveyStore.setSurveyPath(surveyId)
getDetail(surveyId as string) getDetail(surveyId as string)
}) })
@ -54,7 +53,7 @@ const getDetail = async (surveyPath: string) => {
} else { } else {
const res: any = await getPublishedSurveyInfo({ surveyPath }) const res: any = await getPublishedSurveyInfo({ surveyPath })
loadData(res, surveyPath) loadData(res, surveyPath)
store.dispatch('getEncryptInfo') surveyStore.getEncryptInfo()
} }
} catch (error: any) { } catch (error: any) {
console.log(error) console.log(error)

View File

@ -20,7 +20,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useStore } from 'vuex' import { storeToRefs } from 'pinia'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
// @ts-ignore // @ts-ignore
import communalLoader from '@materials/communals/communalLoader.js' import communalLoader from '@materials/communals/communalLoader.js'
@ -29,6 +29,8 @@ import AlertDialog from '../components/AlertDialog.vue'
import ConfirmDialog from '../components/ConfirmDialog.vue' import ConfirmDialog from '../components/ConfirmDialog.vue'
import ProgressBar from '../components/ProgressBar.vue' import ProgressBar from '../components/ProgressBar.vue'
import { useSurveyStore } from '../stores/survey'
import { useQuestionStore } from '../stores/question'
import { submitForm } from '../api/survey' import { submitForm } from '../api/survey'
import encrypt from '../utils/encrypt' import encrypt from '../utils/encrypt'
@ -55,15 +57,13 @@ const boxRef = ref<HTMLElement>()
const alert = useCommandComponent(AlertDialog) const alert = useCommandComponent(AlertDialog)
const confirm = useCommandComponent(ConfirmDialog) const confirm = useCommandComponent(ConfirmDialog)
const store = useStore()
const router = useRouter() const router = useRouter()
const surveyStore = useSurveyStore()
const questionStore = useQuestionStore()
const bannerConf = computed(() => store.state?.bannerConf || {}) const renderData = computed(() => questionStore.renderData)
const renderData = computed(() => store.getters.renderData) const { bannerConf, submitConf, bottomConf: logoConf, whiteData } = storeToRefs(surveyStore)
const submitConf = computed(() => store.state?.submitConf || {}) const surveyPath = computed(() => surveyStore.surveyPath || '')
const logoConf = computed(() => store.state?.bottomConf || {})
const surveyPath = computed(() => store.state?.surveyPath || '')
const whiteData = computed(() => store.state?.whiteData || {})
const validate = (cbk: (v: boolean) => void) => { const validate = (cbk: (v: boolean) => void) => {
const index = 0 const index = 0
@ -71,9 +71,9 @@ const validate = (cbk: (v: boolean) => void) => {
} }
const normalizationRequestBody = () => { const normalizationRequestBody = () => {
const enterTime = store.state.enterTime const enterTime = surveyStore.enterTime
const encryptInfo = store.state.encryptInfo const encryptInfo = surveyStore.encryptInfo as any
const formValues = store.state.formValues const formValues = surveyStore.formValues
const result: any = { const result: any = {
surveyPath: surveyPath.value, surveyPath: surveyPath.value,
@ -84,7 +84,7 @@ const normalizationRequestBody = () => {
} }
if (encryptInfo?.encryptType) { if (encryptInfo?.encryptType) {
result.encryptType = encryptInfo?.encryptType result.encryptType = encryptInfo.encryptType
result.data = encrypt[result.encryptType as 'rsa']({ result.data = encrypt[result.encryptType as 'rsa']({
data: result.data, data: result.data,
secretKey: encryptInfo?.data?.secretKey secretKey: encryptInfo?.data?.secretKey
@ -121,7 +121,7 @@ const submitSurver = async () => {
} }
const handleSubmit = () => { const handleSubmit = () => {
const confirmAgain = store.state.submitConf.confirmAgain const confirmAgain = (surveyStore.submitConf as any).confirmAgain
const { again_text, is_again } = confirmAgain const { again_text, is_again } = confirmAgain
if (is_again) { if (is_again) {

View File

@ -11,16 +11,16 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useStore } from 'vuex' import { useSurveyStore } from '../stores/survey'
// @ts-ignore // @ts-ignore
import communalLoader from '@materials/communals/communalLoader.js' import communalLoader from '@materials/communals/communalLoader.js'
const LogoIcon = communalLoader.loadComponent('LogoIcon') const LogoIcon = communalLoader.loadComponent('LogoIcon')
const store = useStore() const surveyStore = useSurveyStore()
const logoConf = computed(() => store.state?.bottomConf || {}) const logoConf = computed(() => surveyStore?.bottomConf || {})
const successMsg = computed(() => { const successMsg = computed(() => {
const msgContent = store.state?.submitConf?.msgContent || {} const msgContent = (surveyStore?.submitConf as any)?.msgContent || {}
return msgContent?.msg_200 || '提交成功' return msgContent?.msg_200 || '提交成功'
}) })
</script> </script>

View File

@ -1,177 +0,0 @@
import moment from 'moment'
// 引入中文
import 'moment/locale/zh-cn'
// 设置中文
moment.locale('zh-cn')
import adapter from '../adapter'
import { queryVote, getEncryptInfo } from '@/render/api/survey'
import { RuleMatch } from '@/common/logicEngine/RulesMatch'
/**
* CODE_MAP不从management引入在dev阶段会导致B端 router被加载进而导致C端路由被添加 baseUrl: /management
*/
const CODE_MAP = {
SUCCESS: 200,
ERROR: 500,
NO_AUTH: 403
}
const VOTE_INFO_KEY = 'voteinfo'
import router from '../router'
export default {
// 初始化
init({ commit, dispatch }, { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }) {
commit('setEnterTime')
const { begTime, endTime, answerBegTime, answerEndTime } = baseConf
const { msgContent } = submitConf
const now = Date.now()
if (now < new Date(begTime).getTime()) {
router.push({ name: 'errorPage' })
commit('setErrorInfo', {
errorType: 'overTime',
errorMsg: `<p>问卷未到开始填写时间,暂时无法进行填写<p/>
<p>开始时间为: ${begTime}</p>`
})
return
} else if (now > new Date(endTime).getTime()) {
router.push({ name: 'errorPage' })
commit('setErrorInfo', {
errorType: 'overTime',
errorMsg: msgContent.msg_9001 || '您来晚了,感谢支持问卷~'
})
return
} else if (answerBegTime && answerEndTime) {
const momentNow = moment()
const todayStr = momentNow.format('yyyy-MM-DD')
const momentStartTime = moment(`${todayStr} ${answerBegTime}`)
const momentEndTime = moment(`${todayStr} ${answerEndTime}`)
if (momentNow.isBefore(momentStartTime) || momentNow.isAfter(momentEndTime)) {
router.push({ name: 'errorPage' })
commit('setErrorInfo', {
errorType: 'overTime',
errorMsg: `<p>不在答题时间范围内,暂时无法进行填写<p/>
<p>答题时间为: ${answerBegTime} ~ ${answerEndTime}</p>`
})
return
}
}
router.push({ name: 'renderPage' })
// 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段
const { questionData, questionSeq, rules, formValues } = adapter.generateData({
bannerConf,
baseConf,
bottomConf,
dataConf,
skinConf,
submitConf
})
// 将数据设置到state上
commit('assignState', {
questionData,
questionSeq,
rules,
bannerConf,
baseConf,
bottomConf,
dataConf,
skinConf,
submitConf,
formValues
})
// 获取已投票数据
dispatch('initVoteData')
},
// 用户输入或者选择后,更新表单数据
changeData({ commit }, data) {
commit('changeFormData', data)
},
// 初始化投票题的数据
async initVoteData({ state, commit }) {
const questionData = state.questionData
const surveyPath = state.surveyPath
const fieldList = []
for (const field in questionData) {
const { type } = questionData[field]
if (/vote/.test(type)) {
fieldList.push(field)
}
}
if (fieldList.length <= 0) {
return
}
try {
localStorage.removeItem(VOTE_INFO_KEY)
const voteRes = await queryVote({
surveyPath,
fieldList: fieldList.join(',')
})
if (voteRes.code === 200) {
localStorage.setItem(
VOTE_INFO_KEY,
JSON.stringify({
...voteRes.data
})
)
commit('setVoteMap', voteRes.data)
}
} catch (error) {
console.log(error)
}
},
updateVoteData({ state, commit }, data) {
const { key: questionKey, value: questionVal } = data
// 更新前获取接口缓存在localStorage中的数据
const localData = localStorage.getItem(VOTE_INFO_KEY)
const voteinfo = JSON.parse(localData)
const currentQuestion = state.questionData[questionKey]
const options = currentQuestion.options
const voteTotal = voteinfo?.[questionKey]?.total || 0
let totalPayload = {
questionKey,
voteKey: 'total',
voteValue: voteTotal
}
options.forEach((option) => {
const optionhash = option.hash
const voteCount = voteinfo?.[questionKey]?.[optionhash] || 0
// 如果选中值包含该选项对应voteCount 和 voteTotal + 1
if (
Array.isArray(questionVal) ? questionVal.includes(optionhash) : questionVal === optionhash
) {
const countPayload = {
questionKey,
voteKey: optionhash,
voteValue: voteCount + 1
}
totalPayload.voteValue += 1
commit('updateVoteMapByKey', countPayload)
} else {
const countPayload = {
questionKey,
voteKey: optionhash,
voteValue: voteCount
}
commit('updateVoteMapByKey', countPayload)
}
commit('updateVoteMapByKey', totalPayload)
})
},
async getEncryptInfo({ commit }) {
try {
const res = await getEncryptInfo()
if (res.code === CODE_MAP.SUCCESS) {
commit('setEncryptInfo', res.data)
}
} catch (error) {
console.log(error)
}
},
async initRuleEngine({ commit }, ruleConf) {
const ruleEngine = new RuleMatch(ruleConf)
commit('setRuleEgine', ruleEngine)
}
}

View File

@ -1,30 +0,0 @@
export default {
// 题目列表
renderData: (state) => {
const { questionSeq, questionData } = state
let index = 1
return (
questionSeq &&
questionSeq.reduce((pre, item) => {
const questionArr = []
item.forEach((questionKey) => {
const question = { ...questionData[questionKey] }
// 开启显示序号
if (question.showIndex) {
question.indexNumber = index++
}
questionArr.push(question)
})
if (questionArr && questionArr.length) {
pre.push(questionArr)
}
return pre
}, [])
)
}
}

View File

@ -1,13 +0,0 @@
import { createStore } from 'vuex'
import state from './state'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'
export default createStore({
state,
getters,
mutations,
actions
})

View File

@ -1,56 +0,0 @@
import { forEach, set } from 'lodash-es'
export default {
// 将数据设置到state上
assignState(state, data) {
forEach(data, (value, key) => {
state[key] = value
})
},
setQuestionData(state, data) {
state.questionData = data
},
setErrorInfo(state, { errorType, errorMsg }) {
state.errorInfo = {
errorType,
errorMsg
}
},
changeFormData(state, data) {
let { key, value } = data
// console.log('formValues', key, value)
set(state, `formValues.${key}`, value)
},
changeSelectMoreData(state, data) {
const { key, value, field } = data
set(state, `questionData.${field}.othersValue.${key}`, value)
},
setEnterTime(state) {
state.enterTime = Date.now()
},
setSurveyPath(state, data) {
state.surveyPath = data
},
setVoteMap(state, data) {
state.voteMap = data
},
updateVoteMapByKey(state, data) {
const { questionKey, voteKey, voteValue } = data
// 兼容为空的情况
if (!state.voteMap[questionKey]) {
state.voteMap[questionKey] = {}
}
state.voteMap[questionKey][voteKey] = voteValue
},
setQuestionSeq(state, data) {
state.questionSeq = data
},
setEncryptInfo(state, data) {
state.encryptInfo = data
},
setRuleEgine(state, ruleEngine) {
state.ruleEngine = ruleEngine
},
setWhiteData(state, data) {
state.whiteData = data
}
}

View File

@ -1,19 +0,0 @@
import { isMobile } from '../utils/index'
export default {
surveyPath: '',
questionData: null,
isMobile: isMobile(),
errorInfo: {
errorType: '',
errorMsg: ''
},
enterTime: null,
questionSeq: [], // 题目的顺序,因为可能会有分页的情况,所以是一个二维数组[[qid1, qid2], [qid3,qid4]]
voteMap: {},
encryptInfo: null,
ruleEngine: null,
whiteData: {
}
}

View File

@ -0,0 +1,22 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'
export const useErrorInfo = defineStore('errorInfo', () => {
const errorInfo = ref({
errorType: '',
errorMsg: ''
})
const setErrorInfo = ({ errorType, errorMsg }) => {
errorInfo.value = {
errorType,
errorMsg
}
}
return {
errorInfo,
setErrorInfo
}
})

View File

@ -0,0 +1,157 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { set } from 'lodash-es'
import { useSurveyStore } from '@/render/stores/survey'
import { queryVote } from '@/render/api/survey'
const VOTE_INFO_KEY = 'voteinfo'
export const useQuestionStore = defineStore('question', () => {
const voteMap = ref({})
const questionData = ref(null)
const questionSeq = ref([]) // 题目的顺序,因为可能会有分页的情况,所以是一个二维数组[[qid1, qid2], [qid3,qid4]]
// 题目列表
const renderData = computed(() => {
let index = 1
return (
questionSeq.value &&
questionSeq.value.reduce((pre, item) => {
const questionArr = []
item.forEach((questionKey) => {
const question = { ...questionData.value[questionKey] }
// 开启显示序号
if (question.showIndex) {
question.indexNumber = index++
}
questionArr.push(question)
})
if (questionArr && questionArr.length) {
pre.push(questionArr)
}
return pre
}, [])
)
})
const setQuestionData = (data) => {
questionData.value = data
}
const changeSelectMoreData = (data) => {
const { key, value, field } = data
set(questionData.value, `${field}.othersValue.${key}`, value)
}
const setQuestionSeq = (data) => {
questionSeq.value = data
}
const setVoteMap = (data) => {
voteMap.value = data
}
const updateVoteMapByKey = (data) => {
const { questionKey, voteKey, voteValue } = data
// 兼容为空的情况
if (!voteMap.value[questionKey]) {
voteMap.value[questionKey] = {}
}
voteMap.value[questionKey][voteKey] = voteValue
}
//初始化投票题的数据
const initVoteData = async () => {
const surveyStore = useSurveyStore()
const surveyPath = surveyStore.surveyPath
const fieldList = []
for (const field in questionData.value) {
const { type } = questionData.value[field]
if (/vote/.test(type)) {
fieldList.push(field)
}
}
if (fieldList.length <= 0) {
return
}
try {
localStorage.removeItem(VOTE_INFO_KEY)
const voteRes = await queryVote({
surveyPath,
fieldList: fieldList.join(',')
})
if (voteRes.code === 200) {
localStorage.setItem(
VOTE_INFO_KEY,
JSON.stringify({
...voteRes.data
})
)
setVoteMap(voteRes.data)
}
} catch (error) {
console.log(error)
}
}
const updateVoteData = (data) => {
const { key: questionKey, value: questionVal } = data
// 更新前获取接口缓存在localStorage中的数据
const localData = localStorage.getItem(VOTE_INFO_KEY)
const voteinfo = JSON.parse(localData)
const currentQuestion = questionData.value[questionKey]
const options = currentQuestion.options
const voteTotal = voteinfo?.[questionKey]?.total || 0
let totalPayload = {
questionKey,
voteKey: 'total',
voteValue: voteTotal
}
options.forEach((option) => {
const optionhash = option.hash
const voteCount = voteinfo?.[questionKey]?.[optionhash] || 0
// 如果选中值包含该选项对应voteCount 和 voteTotal + 1
if (
Array.isArray(questionVal) ? questionVal.includes(optionhash) : questionVal === optionhash
) {
const countPayload = {
questionKey,
voteKey: optionhash,
voteValue: voteCount + 1
}
totalPayload.voteValue += 1
updateVoteMapByKey(countPayload)
} else {
const countPayload = {
questionKey,
voteKey: optionhash,
voteValue: voteCount
}
updateVoteMapByKey(countPayload)
}
updateVoteMapByKey(totalPayload)
})
}
return {
voteMap,
questionData,
questionSeq,
renderData,
setQuestionData,
changeSelectMoreData,
setQuestionSeq,
setVoteMap,
updateVoteMapByKey,
initVoteData,
updateVoteData
}
})

View File

@ -0,0 +1,182 @@
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { defineStore } from 'pinia'
import { pick } from 'lodash-es'
import { isMobile as isInMobile } from '@/render/utils/index'
import { getEncryptInfo as getEncryptInfoApi } from '@/render/api/survey'
import { useQuestionStore } from '@/render/stores/question'
import { useErrorInfo } from '@/render/stores/errorInfo'
import moment from 'moment'
// 引入中文
import 'moment/locale/zh-cn'
// 设置中文
moment.locale('zh-cn')
import adapter from '../adapter'
/**
* CODE_MAP不从management引入在dev阶段会导致B端 router被加载进而导致C端路由被添加 baseUrl: /management
*/
const CODE_MAP = {
SUCCESS: 200,
ERROR: 500,
NO_AUTH: 403
}
export const useSurveyStore = defineStore('survey', () => {
const surveyPath = ref('')
const isMobile = ref(isInMobile())
const enterTime = ref(0)
const encryptInfo = ref(null)
const rules = ref({})
const bannerConf = ref({})
const baseConf = ref({})
const bottomConf = ref({})
const dataConf = ref({})
const skinConf = ref({})
const submitConf = ref({})
const formValues = ref({})
const whiteData = ref({})
const router = useRouter()
const questionStore = useQuestionStore()
const { setErrorInfo } = useErrorInfo()
const setWhiteData = (data) => {
whiteData.value = data
}
const setSurveyPath = (data) => {
surveyPath.value = data
}
const setEnterTime = () => {
enterTime.value = Date.now()
}
const getEncryptInfo = async () => {
try {
const res = await getEncryptInfoApi()
if (res.code === CODE_MAP.SUCCESS) {
encryptInfo.value = res.data
}
} catch (error) {
console.log(error)
}
}
const canFillQuestionnaire = (baseConf, submitConf) => {
const { begTime, endTime, answerBegTime, answerEndTime } = baseConf
const { msgContent } = submitConf
const now = Date.now()
let isSuccess = true
if (now < new Date(begTime).getTime()) {
isSuccess = false
setErrorInfo({
errorType: 'overTime',
errorMsg: `<p>问卷未到开始填写时间,暂时无法进行填写<p/>
<p>开始时间为: ${begTime}</p>`
})
} else if (now > new Date(endTime).getTime()) {
isSuccess = false
setErrorInfo({
errorType: 'overTime',
errorMsg: msgContent.msg_9001 || '您来晚了,感谢支持问卷~'
})
} else if (answerBegTime && answerEndTime) {
const momentNow = moment()
const todayStr = momentNow.format('yyyy-MM-DD')
const momentStartTime = moment(`${todayStr} ${answerBegTime}`)
const momentEndTime = moment(`${todayStr} ${answerEndTime}`)
if (momentNow.isBefore(momentStartTime) || momentNow.isAfter(momentEndTime)) {
isSuccess = false
setErrorInfo({
errorType: 'overTime',
errorMsg: `<p>不在答题时间范围内,暂时无法进行填写<p/>
<p>答题时间为: ${answerBegTime} ~ ${answerEndTime}</p>`
})
}
}
if (!isSuccess) {
router.push({ name: 'errorPage' })
}
return isSuccess
}
const initSurvey = (option) => {
setEnterTime()
if (!canFillQuestionnaire(option.baseConf, option.submitConf)) {
return
}
// 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段
const {
questionData,
questionSeq,
rules: _rules,
formValues: _formValues
} = adapter.generateData(
pick(option, [
'bannerConf',
'baseConf',
'bottomConf',
'dataConf',
'skinConf',
'submitConf',
'whiteData'
])
)
questionStore.questionData = questionData
questionStore.questionSeq = questionSeq
// 将数据设置到state上
rules.value = _rules
bannerConf.value = option.bannerConf
baseConf.value = option.baseConf
bottomConf.value = option.bottomConf
dataConf.value = option.dataConf
skinConf.value = option.skinConf
submitConf.value = option.submitConf
formValues.value = _formValues
whiteData.value = option.whiteData
// 获取已投票数据
questionStore.initVoteData()
}
// 用户输入或者选择后,更新表单数据
const changeData = (data) => {
let { key, value } = data
if (key in formValues.value) {
formValues.value[key] = value
}
}
return {
surveyPath,
isMobile,
enterTime,
encryptInfo,
rules,
bannerConf,
baseConf,
bottomConf,
dataConf,
skinConf,
submitConf,
formValues,
whiteData,
initSurvey,
changeData,
setWhiteData,
setSurveyPath,
setEnterTime,
getEncryptInfo
}
})

View File

@ -61,7 +61,13 @@ export default defineConfig({
'clipboard', 'clipboard',
'qrcode', 'qrcode',
'moment', 'moment',
'moment/locale/zh-cn' 'moment/locale/zh-cn',
'echarts',
'nanoid',
'yup',
'crypto-js/sha256',
'element-plus/es/locale/lang/zh-cn',
'node-forge'
] ]
}, },
plugins: [ plugins: [