feat:新增分页功能 (#382)

* feat:新增分页功能

* 修复type-check检查

* fix: server  type-check

* fix:修改服务端测试用例

* fix:修复分页bug
This commit is contained in:
chaorenluo 2024-08-06 17:30:12 +08:00 committed by sudoooooo
parent d9e9770eb8
commit c330e6000d
23 changed files with 832 additions and 135 deletions

View File

@ -106,12 +106,13 @@ describe('DataStatisticController', () => {
field: 'xxx', field: 'xxx',
title: 'xxx', title: 'xxx',
type: 'xxx', type: 'xxx',
diffTime: 'xxx',
othersCode: 'xxx', othersCode: 'xxx',
}, },
], ],
listBody: [ listBody: [
{ difTime: '0.5', createDate: '2024-02-11' }, { diffTime: '0.5', createDate: '2024-02-11' },
{ difTime: '0.5', createDate: '2024-02-11' }, { diffTime: '0.5', createDate: '2024-02-11' },
], ],
}; };
@ -151,12 +152,13 @@ describe('DataStatisticController', () => {
field: 'xxx', field: 'xxx',
title: 'xxx', title: 'xxx',
type: 'xxx', type: 'xxx',
diffTime: 'xxx',
othersCode: 'xxx', othersCode: 'xxx',
}, },
], ],
listBody: [ listBody: [
{ difTime: '0.5', createDate: '2024-02-11', data123: '15200000000' }, { diffTime: '0.5', createDate: '2024-02-11', data123: '15200000000' },
{ difTime: '0.5', createDate: '2024-02-11', data123: '13800000000' }, { diffTime: '0.5', createDate: '2024-02-11', data123: '13800000000' },
], ],
}; };

View File

@ -70,7 +70,7 @@ describe('DataStatisticService', () => {
data413: 3, data413: 3,
data863: '109239', data863: '109239',
}, },
difTime: 21278, diffTime: 21278,
clientTime: 1710340862733.0, clientTime: 1710340862733.0,
secretKeys: [], secretKeys: [],
optionTextAndId: { optionTextAndId: {
@ -197,7 +197,7 @@ describe('DataStatisticService', () => {
data413_3: expect.any(String), data413_3: expect.any(String),
data413: expect.any(Number), data413: expect.any(Number),
data863: expect.any(String), data863: expect.any(String),
difTime: expect.any(String), diffTime: expect.any(String),
createDate: expect.any(String), createDate: expect.any(String),
}), }),
]), ]),
@ -220,7 +220,7 @@ describe('DataStatisticService', () => {
'U2FsdGVkX19bRmf3uEmXAJ/6zXd1Znr3cZsD5v4Nocr2v5XG1taXluz8cohFkDyH', 'U2FsdGVkX19bRmf3uEmXAJ/6zXd1Znr3cZsD5v4Nocr2v5XG1taXluz8cohFkDyH',
data770: 'U2FsdGVkX18ldQMhJjFXO8aerjftZLpFnRQ4/FVcCLI=', data770: 'U2FsdGVkX18ldQMhJjFXO8aerjftZLpFnRQ4/FVcCLI=',
}, },
difTime: 806707, diffTime: 806707,
clientTime: 1710400229573.0, clientTime: 1710400229573.0,
secretKeys: ['data458', 'data450', 'data405', 'data770'], secretKeys: ['data458', 'data450', 'data405', 'data770'],
optionTextAndId: { optionTextAndId: {
@ -303,7 +303,7 @@ describe('DataStatisticService', () => {
data458: expect.any(String), data458: expect.any(String),
data515: expect.any(String), data515: expect.any(String),
data770: expect.any(String), data770: expect.any(String),
difTime: expect.any(String), diffTime: expect.any(String),
}), }),
]), ]),
); );

View File

@ -49,6 +49,7 @@
"opacity": 100 "opacity": 100
} }
}, },
"pagingConf":[],
"logicConf": { "logicConf": {
"showLogicConf": [] "showLogicConf": []
} }

View File

@ -32,11 +32,11 @@ const mockDecryptErrorBody = {
'SkyfsbS6MDvFrrxFJQDMxsvm53G3PTURktfZikJP2fKilC8wPW5ZdfX29Fixor5ldHBBNyILsDtxhbNahEbNCDw8n1wS8IIckFuQcaJtn6MLOD+h+Iuywka3ig4ecTN87RpdcfEQe7f38rSSx0zoFU8j37eojjSF7eETBSrz5m9WaNesQo4hhC6p7wmAo1jggkdbG8PVrFqrZPbkHN5jOBrzQEqdqYu9A5wHMM7nUteqlPpkiogEDYmBIccqmPdtO54y7LoPslXgXj6jNja8oVNaYlnp7UsisT+i5cuQ7lbDukEhrfpAIFRsT2IUwVlLjWHtFQm4/4I5HyvVBirTng==', 'SkyfsbS6MDvFrrxFJQDMxsvm53G3PTURktfZikJP2fKilC8wPW5ZdfX29Fixor5ldHBBNyILsDtxhbNahEbNCDw8n1wS8IIckFuQcaJtn6MLOD+h+Iuywka3ig4ecTN87RpdcfEQe7f38rSSx0zoFU8j37eojjSF7eETBSrz5m9WaNesQo4hhC6p7wmAo1jggkdbG8PVrFqrZPbkHN5jOBrzQEqdqYu9A5wHMM7nUteqlPpkiogEDYmBIccqmPdtO54y7LoPslXgXj6jNja8oVNaYlnp7UsisT+i5cuQ7lbDukEhrfpAIFRsT2IUwVlLjWHtFQm4/4I5HyvVBirTng==',
'IMn0E7R6cYCQPI497mz3x99CPA4cImAFEfIv8Q98Gm5bFcgKJX6KFYS7PF/VtIuI1leKwwNYexQy7+2HnF40by/huVugoPYnPd4pTpUdG6f1kh8EpzIir2+8P98Dcz2+NZ/khP2RIAM8nOr+KSC99TNGhuKaKQCItyLFDkr80s3zv+INieGc8wULIrGoWDJGN2KdU/jSq+hkV0QXypd81N5IyAoNhZLkZeM/FU6grGFPnGRtcDDc5W8YWVHO87VymlxPCTRawXRTDcvGIUqb3GuZfxvA7AULqbspmN9kzt3rktuZLNb2TFQDsJfqUuCmi+b28qP/G4OrT9/VAHhYKw==', 'IMn0E7R6cYCQPI497mz3x99CPA4cImAFEfIv8Q98Gm5bFcgKJX6KFYS7PF/VtIuI1leKwwNYexQy7+2HnF40by/huVugoPYnPd4pTpUdG6f1kh8EpzIir2+8P98Dcz2+NZ/khP2RIAM8nOr+KSC99TNGhuKaKQCItyLFDkr80s3zv+INieGc8wULIrGoWDJGN2KdU/jSq+hkV0QXypd81N5IyAoNhZLkZeM/FU6grGFPnGRtcDDc5W8YWVHO87VymlxPCTRawXRTDcvGIUqb3GuZfxvA7AULqbspmN9kzt3rktuZLNb2TFQDsJfqUuCmi+b28qP/G4OrT9/VAHhYKw==',
], ],
difTime: 806707, diffTime: 806707,
clientTime: 1710400229573, clientTime: 1710400229573,
encryptType: 'rsa', encryptType: 'rsa',
sessionId: '65f2664c92862d6a9067ad18', sessionId: '65f2664c92862d6a9067ad18',
sign: '8c9ca8804c9d94de6055d68a1f3c423fe50c95b4bd69f809ee2da8fcd82fd960.1710400229589', sign: '95d6ff5dd3d9ddc205cbab88defe40ebe889952961f1d60e760fa411e2cb39fe.1710400229589',
}; };
const mockSubmitData = { const mockSubmitData = {
@ -45,11 +45,11 @@ const mockSubmitData = {
'SkyfsbS6MDvFrrxFJQDMxsvm53G3PTURktfZikJP2fKilC8wPW5ZdfX29Fixor5ldHBBNyILsDtxhbNahEbNCDw8n1wS8IIckFuQcaJtn6MLOD+h+Iuywka3ig4ecTN87RpdcfEQe7f38rSSx0zoFU8j37eojjSF7eETBSrz5m9WaNesQo4hhC6p7wmAo1jggkdbG8PVrFqrZPbkHN5jOBrzQEqdqYu9A5wHMM7nUteqlPpkiogEDYmBIccqmPdtO54y7LoPslXgXj6jNja8oVNaYlnp7UsisT+i5cuQ7lbDukEhrfpAIFRsT2IUwVlLjWHtFQm4/4I5HyvVBirTng==', 'SkyfsbS6MDvFrrxFJQDMxsvm53G3PTURktfZikJP2fKilC8wPW5ZdfX29Fixor5ldHBBNyILsDtxhbNahEbNCDw8n1wS8IIckFuQcaJtn6MLOD+h+Iuywka3ig4ecTN87RpdcfEQe7f38rSSx0zoFU8j37eojjSF7eETBSrz5m9WaNesQo4hhC6p7wmAo1jggkdbG8PVrFqrZPbkHN5jOBrzQEqdqYu9A5wHMM7nUteqlPpkiogEDYmBIccqmPdtO54y7LoPslXgXj6jNja8oVNaYlnp7UsisT+i5cuQ7lbDukEhrfpAIFRsT2IUwVlLjWHtFQm4/4I5HyvVBirTng==',
'IMn0E7R6cYCQPI497mz3x99CPA4cImAFEfIv8Q98Gm5bFcgKJX6KFYS7PF/VtIuI1leKwwNYexQy7+2HnF40by/huVugoPYnPd4pTpUdG6f1kh8EpzIir2+8P98Dcz2+NZ/khP2RIAM8nOr+KSC99TNGhuKaKQCItyLFDkr80s3zv+INieGc8wULIrGoWDJGN2KdU/jSq+hkV0QXypd81N5IyAoNhZLkZeM/FU6grGFPnGRtcDDc5W8YWVHO87VymlxPCTRawXRTDcvGIUqb3GuZfxvA7AULqbspmN9kzt3rktuZLNb2TFQDsJfqUuCmi+b28qP/G4OrT9/VAHhYKw==', 'IMn0E7R6cYCQPI497mz3x99CPA4cImAFEfIv8Q98Gm5bFcgKJX6KFYS7PF/VtIuI1leKwwNYexQy7+2HnF40by/huVugoPYnPd4pTpUdG6f1kh8EpzIir2+8P98Dcz2+NZ/khP2RIAM8nOr+KSC99TNGhuKaKQCItyLFDkr80s3zv+INieGc8wULIrGoWDJGN2KdU/jSq+hkV0QXypd81N5IyAoNhZLkZeM/FU6grGFPnGRtcDDc5W8YWVHO87VymlxPCTRawXRTDcvGIUqb3GuZfxvA7AULqbspmN9kzt3rktuZLNb2TFQDsJfqUuCmi+b28qP/G4OrT9/VAHhYKw==',
], ],
difTime: 806707, diffTime: 806707,
clientTime: 1710400229573, clientTime: 1710400229573,
encryptType: 'rsa', encryptType: 'rsa',
sessionId: '65f29fc192862d6a9067ad28', sessionId: '65f29fc192862d6a9067ad28',
sign: '8c9ca8804c9d94de6055d68a1f3c423fe50c95b4bd69f809ee2da8fcd82fd960.1710400229589', sign: '95d6ff5dd3d9ddc205cbab88defe40ebe889952961f1d60e760fa411e2cb39fe.1710400229589',
}; };
const mockClientEncryptInfo = { const mockClientEncryptInfo = {
@ -185,7 +185,7 @@ describe('SurveyResponseController', () => {
status: RECORD_STATUS.NEW, status: RECORD_STATUS.NEW,
date: 1711025113146, date: 1711025113146,
}, },
difTime: 30518, diffTime: 30518,
data: { data: {
data458: '15000000000', data458: '15000000000',
data515: '115019', data515: '115019',
@ -220,7 +220,6 @@ describe('SurveyResponseController', () => {
jest jest
.spyOn(clientEncryptService, 'deleteEncryptInfo') .spyOn(clientEncryptService, 'deleteEncryptInfo')
.mockResolvedValueOnce(undefined); .mockResolvedValueOnce(undefined);
const result = await controller.createResponse(reqBody, {}); const result = await controller.createResponse(reqBody, {});
expect(result).toEqual({ code: 200, msg: '提交成功' }); expect(result).toEqual({ code: 200, msg: '提交成功' });
@ -240,7 +239,7 @@ describe('SurveyResponseController', () => {
data770: '123456@qq.com', data770: '123456@qq.com',
}, },
clientTime: reqBody.clientTime, clientTime: reqBody.clientTime,
difTime: reqBody.difTime, diffTime: reqBody.diffTime,
surveyId: mockResponseSchema.pageId, surveyId: mockResponseSchema.pageId,
optionTextAndId: { optionTextAndId: {
data515: [ data515: [
@ -327,7 +326,7 @@ describe('SurveyResponseController', () => {
const reqBody = { const reqBody = {
...mockSubmitData, ...mockSubmitData,
password: '123457', password: '123457',
sign: '4ff02062141d92d80629eae4797ba68056f29a9709cdf59bf206776fc0971c1a.1710400229589', sign: '145595d85079af3b1fb30784177c348555f442837c051d90f57a01ce1ff53c32.1710400229589',
}; };
jest jest

View File

@ -33,7 +33,7 @@ describe('SurveyResponseService', () => {
const surveyData = { const surveyData = {
data: {}, data: {},
clientTime: new Date(), clientTime: new Date(),
difTime: 0, diffTime: 0,
surveyId: 'testId', surveyId: 'testId',
surveyPath: 'testPath', surveyPath: 'testPath',
optionTextAndId: {}, optionTextAndId: {},
@ -59,7 +59,7 @@ describe('SurveyResponseService', () => {
surveyPath: surveyData.surveyPath, surveyPath: surveyData.surveyPath,
data: surveyData.data, data: surveyData.data,
clientTime: surveyData.clientTime, clientTime: surveyData.clientTime,
difTime: surveyData.difTime, diffTime: surveyData.diffTime,
pageId: surveyData.surveyId, pageId: surveyData.surveyId,
secretKeys: [], secretKeys: [],
optionTextAndId: surveyData.optionTextAndId, optionTextAndId: surveyData.optionTextAndId,

4
web/components.d.ts vendored
View File

@ -17,6 +17,7 @@ declare module 'vue' {
ElDialog: typeof import('element-plus/es')['ElDialog'] ElDialog: typeof import('element-plus/es')['ElDialog']
ElForm: typeof import('element-plus/es')['ElForm'] ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenu: typeof import('element-plus/es')['ElMenu']
@ -42,6 +43,8 @@ declare module 'vue' {
ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree'] ElTree: typeof import('element-plus/es')['ElTree']
IEpArrowLeft: typeof import('~icons/ep/arrow-left')['default']
IEpArrowRight: typeof import('~icons/ep/arrow-right')['default']
IEpBottom: typeof import('~icons/ep/bottom')['default'] IEpBottom: typeof import('~icons/ep/bottom')['default']
IEpCheck: typeof import('~icons/ep/check')['default'] IEpCheck: typeof import('~icons/ep/check')['default']
IEpCirclePlus: typeof import('~icons/ep/circle-plus')['default'] IEpCirclePlus: typeof import('~icons/ep/circle-plus')['default']
@ -54,6 +57,7 @@ declare module 'vue' {
IEpMinus: typeof import('~icons/ep/minus')['default'] IEpMinus: typeof import('~icons/ep/minus')['default']
IEpMonitor: typeof import('~icons/ep/monitor')['default'] IEpMonitor: typeof import('~icons/ep/monitor')['default']
IEpMore: typeof import('~icons/ep/more')['default'] IEpMore: typeof import('~icons/ep/more')['default']
IEpMoreFilled: typeof import('~icons/ep/more-filled')['default']
IEpPlus: typeof import('~icons/ep/plus')['default'] IEpPlus: typeof import('~icons/ep/plus')['default']
IEpQuestionFilled: typeof import('~icons/ep/question-filled')['default'] IEpQuestionFilled: typeof import('~icons/ep/question-filled')['default']
IEpRank: typeof import('~icons/ep/rank')['default'] IEpRank: typeof import('~icons/ep/rank')['default']

View File

@ -1 +1,2 @@
export const DND_GROUP = 'question' export const DND_GROUP = 'question'
export const QUESTION_CATALOG = 'questionCatalog'

View File

@ -53,6 +53,7 @@ export const defaultQuestionConfig = {
star: 5, star: 5,
optionOrigin: '', optionOrigin: '',
originType: 'selected', originType: 'selected',
innerType:'',
matrixOptionsRely: '', matrixOptionsRely: '',
numberRange: { numberRange: {
min: { min: {

View File

@ -14,8 +14,9 @@
:ref="`questionWrapper-${element.field}`" :ref="`questionWrapper-${element.field}`"
:moduleConfig="element" :moduleConfig="element"
:qIndex="element.qIndex" :qIndex="element.qIndex"
:isFirst="index==0"
:indexNumber="element.indexNumber" :indexNumber="element.indexNumber"
:isSelected="currentEditOne === index" :isSelected="currentEditOne === element.qIndex"
:isLast="index + 1 === questionDataList.length" :isLast="index + 1 === questionDataList.length"
@select="handleSelect" @select="handleSelect"
@changeSeq="handleChangeSeq" @changeSeq="handleChangeSeq"
@ -24,7 +25,7 @@
:type="element.type" :type="element.type"
:moduleConfig="element" :moduleConfig="element"
:indexNumber="element.indexNumber" :indexNumber="element.indexNumber"
:isSelected="currentEditOne === index" :isSelected="currentEditOne === element.qIndex"
:readonly="true" :readonly="true"
@change="handleChange" @change="handleChange"
></QuestionContainerB> ></QuestionContainerB>
@ -39,7 +40,6 @@ 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'
import { filterQuestionPreviewData } from '@/management/utils/index'
import { DND_GROUP } from '@/management/config/dnd' import { DND_GROUP } from '@/management/config/dnd'
export default defineComponent({ export default defineComponent({
@ -60,15 +60,15 @@ export default defineComponent({
} }
} }
}, },
emits: ['change', 'select', 'changeSeq'], emits: ['change', 'select', 'changeSeq','change'],
setup(props, { emit }) { setup(props, { emit }) {
const editStore = useEditStore() const editStore = useEditStore()
const renderData = computed({ const renderData = computed({
get() { get() {
return filterQuestionPreviewData(props.questionDataList) return props.questionDataList; //filterQuestionPreviewData(props.questionDataList)
}, },
set(value) { set(value) {
editStore.setQuestionDataList(value) editStore.moveQuestionDataList(value)
} }
}) })
const handleSelect = (index) => { const handleSelect = (index) => {

View File

@ -0,0 +1,101 @@
<template>
<div class="pagination-wrap">
<PaginationPanel v-model="schema.pagingEditOne" :readonly="props.readonly" :totalPage="pagingCount" @changePage="updatePageEditOne" :intervalCount="10">
<template #tooltip="{index}">
<div>
<div v-if="index != 1" class="controls-wrap-item" @click="movePaging(index, 'up')">前移一页</div>
<div v-if="index != pagingCount" class="mt8 controls-wrap-item" @click="movePaging(index, 'down')">后移一页</div>
<div class="mt8 controls-wrap-item" @click="copyPaging(index)">复制</div>
<div class="mt8 controls-wrap-item" @click="deletePaging(index)">删除</div>
</div>
</template>
</PaginationPanel>
<i-ep-plus v-if="!props.readonly" style="font-size: 12px;" @click="addPagingControls" class="plus-add" />
</div>
</template>
<script setup>
import PaginationPanel from '../modules/pagingModule/PaginationPanel.vue'
import { useEditStore } from '@/management/stores/edit'
import { storeToRefs } from 'pinia'
import { QUESTION_TYPE } from '@/common/typeEnum.ts'
const props = defineProps({
readonly: {
type: Boolean,
default: false
}
})
const editStore = useEditStore();
const { pagingCount, schema, newQuestionIndex } = storeToRefs(editStore)
const { updatePagingEditOne, addPaging, createNewQuestion, addQuestion, setCurrentEditOne, deletePaging, swapArrayRanges, copyPaging } = editStore;
const updatePageEditOne = (index) => {
setCurrentEditOne(null)
updatePagingEditOne(index);
}
const movePaging = (position, type) => {
setCurrentEditOne(null)
const pagingIndex = type === 'up' ? position - 1 : position + 1;
updatePagingEditOne(pagingIndex)
if (type === 'up') {
swapArrayRanges(position, position - 1)
}
if (type === 'down') {
swapArrayRanges(position + 1, position)
}
}
const addPagingControls = () => {
const newQuestion = createNewQuestion({ type: QUESTION_TYPE.TEXT })
updatePagingEditOne(pagingCount.value + 1)
setCurrentEditOne(null)
addQuestion({ question: newQuestion, index: newQuestionIndex.value })
setCurrentEditOne(newQuestionIndex.value)
addPaging()
}
</script>
<style lang="scss" scoped>
.mt8 {
margin-top: 8px
}
.controls-wrap {
&-item {
color: #4A4C5B;
font-size: 12px;
font-weight: 400;
cursor: pointer;
&:hover {
color: $primary-color;
}
}
}
.pagination-wrap {
display: flex;
align-items: center;
background: #FFFFFF;
box-shadow: 0px 2px 10px -2px rgba(82, 82, 102, 0.2);
border-radius: 4px;
margin-bottom: 12px;
.plus-add {
cursor: pointer;
margin-left: 12px;
&:hover {
color: $primary-color;
}
}
}
</style>

View File

@ -51,6 +51,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false default: false
}, },
isFirst: {
type: Boolean,
default: false
},
isLast: { isLast: {
type: Boolean, type: Boolean,
default: false default: false
@ -81,7 +85,7 @@ const showHover = computed(() => {
return isHover.value || props.isSelected return isHover.value || props.isSelected
}) })
const showUp = computed(() => { const showUp = computed(() => {
return props.qIndex !== 0 return !props.isFirst
}) })
const showDown = computed(() => { const showDown = computed(() => {
return !props.isLast return !props.isLast

View File

@ -10,6 +10,7 @@ export default function (schema) {
'skinConf', 'skinConf',
'submitConf', 'submitConf',
'questionDataList', 'questionDataList',
"pagingConf",
'logicConf' 'logicConf'
]) ])
configData.dataConf = { configData.dataConf = {

View File

@ -0,0 +1,282 @@
<template>
<div v-if="props.totalPage > 0" class="com-pagination">
<span :class="['com-pagination-item', prev_class]" @click="changePage(prev_page)">
<i-ep-ArrowLeft />
</span>
<template v-if="!is_more_filled">
<div v-for="i in firstPagination" :key="i" :class="['com-pagination-item', `page-${i}`, (now_page == i ? 'current' : '')]"
@click="changePage(i)">
<span>{{ i }}</span>
<div v-if="!props.readonly" :class="['moreControls']" @click.stop="showTooltipVisible(i)">
<i-ep-MoreFilled />
</div>
</div>
</template>
<template v-else>
<div v-for="i in more_filled_arr.startArr" @click="changePage(i)" :key="i"
:class="['com-pagination-item', ` page-${i}`, (now_page == i ? 'current' : '')]">
<span>{{ i }}</span>
<div v-if="!props.readonly" :class="['moreControls']" @click.stop="showTooltipVisible(i)">
<i-ep-MoreFilled />
</div>
</div>
<el-tooltip class="controls-wrap" effect="light" placement="bottom" :visible="moreVisible">
<span class="com-pagination-item" @click.stop="moreVisible = true">
<i-ep-MoreFilled />
</span>
<template #content>
<div class="bubble-wrap">
<div class="bubble-item" v-for="i in more_filled_arr.bubbleArr" :key="i" @click="changePage(i)">
<span>{{ i }}</span>
</div>
</div>
</template>
</el-tooltip>
<div v-for="i in more_filled_arr.endArr" :key="i"
:class="['com-pagination-item', `page-${i}`, (now_page == i ? 'current' : '')]" @click="changePage(i)">
<span>{{ i }}</span>
<div v-if="!props.readonly" :class="['moreControls']" @click.stop="showTooltipVisible(i)">
<i-ep-MoreFilled />
</div>
</div>
</template>
<span :class="['com-pagination-item', next_class]" @click="changePage(next_page)">
<i-ep-ArrowRight />
</span>
<el-tooltip v-if="slot.tooltip && props.readonly==false" :visible="tooltipVisible" :popper-options="{
modifiers: [
{
name: 'computeStyles',
options: {
adaptive: false,
enabled: false,
},
},
],
}" :virtual-ref="triggerBtn" virtual-triggering effect="light" popper-class="singleton-tooltip">
<template #content>
<slot name="tooltip" :index="tooltipIndex"></slot>
</template>
</el-tooltip>
</div>
</template>
<script setup lang="ts">
import { clone } from "lodash-es";
import { withDefaults, reactive, computed, watch, ref, onMounted, onUnmounted, nextTick, useSlots } from "vue";
interface Props {
modelValue: number //
totalPage?: number
intervalCount?: number,
readonly?: boolean
}
const props = withDefaults(defineProps<Props>(), {
modelValue: 1,
totalPage: 1,
intervalCount: 8,
readonly:false
});
const emit = defineEmits(['change-page', 'update:modelValue']);
const state = reactive({
now: props.modelValue,
jump: ''
});
const slot = useSlots();
const moreVisible = ref(false)
const tooltipVisible = ref(false)
const triggerBtn = ref<EventTarget | null>(null);
const tooltipIndex = ref(0)
const now_page = computed(() => {
return state.now * 1;
});
const prev_class = computed(() => {
return now_page.value == 1 ? 'disabled' : '';
});
const next_class = computed(() => {
return now_page.value == props.totalPage ? 'disabled' : '';
});
const prev_page = computed(() => {
return now_page.value > 1 ? now_page.value - 1 : 1;
});
const next_page = computed(() => {
return now_page.value < props.totalPage ? now_page.value + 1 : props.totalPage;
});
const is_more_filled = computed(() => {
const intervalNum = props.totalPage - now_page.value + 1;
if (intervalNum >= props.intervalCount+1) {
return true;
}
return false;
})
const totalArr = computed(() => {
const arr = [];
for (let i = 0; i < props.totalPage; i++) {
arr.push(i + 1);
}
return arr;
})
const more_filled_arr = computed(() => {
let startArr = [];
let bubbleArr = [];
let endArr = [];
const arr = clone(totalArr.value);
const intervalNum = Math.round(props.intervalCount/2)
startArr = arr.slice(now_page.value - 1, intervalNum+now_page.value - 1);
endArr = arr.slice(intervalNum*-1)
bubbleArr = arr.slice(startArr[startArr.length-1], endArr[0]-1);
return {
startArr,
bubbleArr,
endArr
}
})
const firstPagination = computed(() => {
const arr = clone(totalArr.value);
return arr.splice(props.intervalCount * -1);
})
const changePage = (page: number) => {
state.now = page;
emit('update:modelValue', state.now);
emit('change-page', state.now);
};
const showTooltipVisible = (index:number) => {
if (slot.tooltip) {
nextTick(() => {
tooltipIndex.value = index;
triggerBtn.value = document.getElementsByClassName(`page-${index}`)[0] || null;
tooltipVisible.value = true;
})
}
}
const hideMoreVisible = () => {
moreVisible.value = false;
}
const hideTooltipVisible = () => {
tooltipVisible.value = false;
}
onMounted(() => {
document.addEventListener('click', hideMoreVisible)
if (slot.tooltip) {
document.addEventListener('click', hideTooltipVisible)
}
});
onUnmounted(() => {
document.removeEventListener('click', hideMoreVisible)
if (slot.tooltip) {
document.removeEventListener('click', hideTooltipVisible)
}
});
watch(() => props.modelValue, () => {
state.now = props.modelValue;
});
</script>
<style lang="scss" scoped>
.bubble-wrap {
.bubble-item {
max-width: 100px;
min-width: 10px;
text-align: center;
cursor: pointer;
padding: 4px 8px;
border-radius: 6px;
&:hover {
background-color: #cccc;
}
}
}
.com-pagination {
display: flex;
justify-content: center;
align-items: center;
font-size: 0;
user-select: none;
.moreControls{
color:#6E707C;
display: none;
position: absolute;
left:22px;
svg{
transform: rotate(90deg);
font-size: 10px;
}
}
&-item {
display: inline-block;
position: relative;
// padding: 0 4px;
min-width: 32px;
height: 32px;
color: #303133;
border-radius: 2px;
cursor: pointer;
text-align: center;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
&:hover {
color: $primary-color;
.moreControls{
display: block;
}
}
&.active {
color: $primary-color;
.moreControls{
display: block;
}
}
svg {
display: block;
font-size: 12px;
font-weight: bold;
width: inherit;
cursor: pointer;
&:hover {
color: $primary-color;
}
}
}
.disabled,
.disabled:hover {
svg{
cursor: not-allowed;
color:#303133 !important;
}
}
.current,
.current:hover {
color: $primary-color;
}
}
</style>

View File

@ -1,8 +1,12 @@
<template> <template>
<div class="main-operation" @click="onMainClick" ref="mainOperation"> <div class="main-operation" @click="onMainClick" ref="mainOperation">
<div class="pagination-wrapper">
<PagingWrapper :readonly="false"/>
</div>
<div class="operation-wrapper" ref="operationWrapper"> <div class="operation-wrapper" ref="operationWrapper">
<div class="box content" ref="box"> <div class="box content" ref="box">
<MainTitle <MainTitle
v-if="pagingEditOne==1"
:bannerConf="bannerConf" :bannerConf="bannerConf"
:readonly="false" :readonly="false"
:is-selected="currentEditOne === 'mainTitle'" :is-selected="currentEditOne === 'mainTitle'"
@ -11,7 +15,7 @@
/> />
<MaterialGroup <MaterialGroup
:current-edit-one="parseInt(currentEditOne)" :current-edit-one="parseInt(currentEditOne)"
:questionDataList="questionDataList" :questionDataList="pagingQuestionData"
@select="setCurrentEditOne" @select="setCurrentEditOne"
@change="handleChange" @change="handleChange"
@changeSeq="onQuestionOperation" @changeSeq="onQuestionOperation"
@ -21,6 +25,7 @@
:submit-conf="submitConf" :submit-conf="submitConf"
:readonly="false" :readonly="false"
:skin-conf="skinConf" :skin-conf="skinConf"
:is-finally-page="isFinallyPage"
:is-selected="currentEditOne === 'submit'" :is-selected="currentEditOne === 'submit'"
@select="setCurrentEditOne('submit')" @select="setCurrentEditOne('submit')"
/> />
@ -32,6 +37,7 @@
<script setup> <script setup>
import { ref, watch, toRefs } from 'vue' import { ref, watch, toRefs } from 'vue'
import communalLoader from '@materials/communals/communalLoader.js' import communalLoader from '@materials/communals/communalLoader.js'
import PagingWrapper from '@/management/pages/edit/components/PagingWrapper.vue'
import MaterialGroup from '@/management/pages/edit/components/MaterialGroup.vue' import MaterialGroup from '@/management/pages/edit/components/MaterialGroup.vue'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { useEditStore } from '@/management/stores/edit' import { useEditStore } from '@/management/stores/edit'
@ -40,13 +46,15 @@ const MainTitle = communalLoader.loadComponent('MainTitle')
const SubmitButton = communalLoader.loadComponent('SubmitButton') const SubmitButton = communalLoader.loadComponent('SubmitButton')
const editStore = useEditStore() const editStore = useEditStore()
const { questionDataList, currentEditOne, currentEditKey } = storeToRefs(editStore) const { currentEditOne, currentEditKey,pagingQuestionData,isFinallyPage,pagingEditOne } = storeToRefs(editStore)
const { schema, changeSchema, moveQuestion, copyQuestion, deleteQuestion, setCurrentEditOne } = const { schema, changeSchema, moveQuestion, copyQuestion, deleteQuestion, setCurrentEditOne } =
editStore editStore
const mainOperation = ref(null) const mainOperation = ref(null)
const materialGroup = ref(null) const materialGroup = ref(null)
const { bannerConf, submitConf, skinConf } = toRefs(schema) const { bannerConf, submitConf, skinConf } = toRefs(schema)
// const autoScrollData = computed(() => { // const autoScrollData = computed(() => {
// return { // return {
// currentEditOne: currentEditOne.value, // currentEditOne: currentEditOne.value,
@ -138,6 +146,13 @@ watch(
align-items: center; align-items: center;
background-color: #f6f7f9; background-color: #f6f7f9;
} }
.pagination-wrapper{
width: 90%;
padding-right: 30px;
margin-right: -30px;
position: relative;
top: 50px;
}
.toolbar { .toolbar {
width: 100%; width: 100%;

View File

@ -1,68 +1,95 @@
<template> <template>
<div class="question-catalog-wrapper"> <div class="question-catalog-wrapper">
<draggable <el-collapse>
:list="renderData" <el-collapse-item v-for="(v, i) in renderData" :key="v" :title="`第${i + 1}页`" :name="i + 1">
@end="handleDragEnd" <draggable v-model="renderData[i]" itemKey="field" :group="QUESTION_CATALOG" handle=".draggHandle"
itemKey="field" host-class="catalog-item-ghost">
handle=".draggHandle" <template #item="{ element }">
host-class="catalog-item-ghost" <CatalogItem :title="element.title" :indexNumber="element.indexNumber" :showIndex="element.showIndex"
> @select="setPagingOneEdit(element.qIndex, i + 1)" />
<template #item="{ element, index }"> </template>
<CatalogItem </draggable>
:title="element.title" </el-collapse-item>
:indexNumber="element.indexNumber" </el-collapse>
:showIndex="element.showIndex"
@select="setCurrentEditOne(index)"
/>
</template>
</draggable>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { watch, ref } from 'vue'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { useEditStore } from '@/management/stores/edit' 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 { QUESTION_CATALOG } from '@/management/config/dnd'
const editStore = useEditStore() const editStore = useEditStore()
const { questionDataList, currentEditOne } = storeToRefs(editStore) const { questionDataList, pagingConf } = storeToRefs(editStore)
const { moveQuestion, setCurrentEditOne } = editStore const { setCurrentEditOne, getPagingQuestionData, updatePagingEditOne,setPaging,compareQuestionSeq } = editStore
const renderData = computed(() => {
return filterQuestionPreviewData(questionDataList.value) || [] const renderData: any = ref([])
const setPagingOneEdit = (qIndex: number, pagingIndex: number) => {
updatePagingEditOne(pagingIndex)
setCurrentEditOne(qIndex)
}
watch(() => [pagingConf.value,questionDataList.value], () => {
renderData.value = [];
for (let index = 0; index < pagingConf.value.length; index++) {
renderData.value.push(getPagingQuestionData(index + 1))
}
},{
deep: true,
immediate: true
}) })
const handleDragEnd = ({ newIndex, oldIndex }: any) => { watch(() => renderData.value, (newVal) => {
if (currentEditOne.value === oldIndex) { if (newVal.length == 0) return;
setCurrentEditOne(newIndex) let pagingData: Array<number> = [];
} let questionList: Array<any> = [];
newVal.map((v:any) => {
moveQuestion({ pagingData.push(v.length)
index: oldIndex, questionList.push(...v)
range: newIndex - oldIndex
}) })
} setPaging(pagingData)
compareQuestionSeq(questionList)
}, {
deep: true
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.question-catalog-wrapper { .question-catalog-wrapper {
padding-bottom: 400px; // padding-bottom: 400px; //
.catelog-first-page { .catelog-first-page {
font-size: 12px; font-size: 12px;
color: #999999; color: #999999;
padding-bottom: 8px; padding-bottom: 8px;
} }
.catalog-item-ghost { .catalog-item-ghost {
&.question-catalog-item { &.question-catalog-item {
.catalog-item { .catalog-item {
color: $normal-color-light; color: $normal-color-light;
.draggHandle { .draggHandle {
color: $normal-color-light; color: $normal-color-light;
} }
} }
} }
} }
:deep(.el-collapse-item__header) {
font-size: 14px;
color: #6E707C;
font-weight: 500;
}
.catalog-item-dragging { .catalog-item-dragging {
opacity: 1; opacity: 1;
background: #ffffff; background: #ffffff;

View File

@ -10,7 +10,7 @@
class="questiontype-list" class="questiontype-list"
:list="item.questionList" :list="item.questionList"
:group="{ name: DND_GROUP, pull: 'clone', put: false }" :group="{ name: DND_GROUP, pull: 'clone', put: false }"
:clone="getNewQuestion" :clone="createNewQuestion"
item-key="path" item-key="path"
> >
<template #item="{ element }"> <template #item="{ element }">
@ -44,44 +44,26 @@ import draggable from 'vuedraggable'
import { DND_GROUP } from '@/management/config/dnd' 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 { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { useEditStore } from '@/management/stores/edit' import { useEditStore } from '@/management/stores/edit'
import { isNumber as _isNumber } from 'lodash-es' import { ref } from 'vue'
import { computed, ref } from 'vue'
import { QUESTION_TYPE } from '@/common/typeEnum.ts'
const editStore = useEditStore() const editStore = useEditStore()
const { questionDataList, currentEditOne } = storeToRefs(editStore) const { newQuestionIndex } = storeToRefs(editStore)
const { addQuestion, setCurrentEditOne } = editStore const { addQuestion, setCurrentEditOne,createNewQuestion } = 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 newQuestionIndex = computed(() => {
const index = _isNumber(currentEditOne.value)
? currentEditOne.value + 1
: questionDataList.value.length
return index
})
questionLoader.init({ questionLoader.init({
typeList: questionTypeList.map((item) => item.type) typeList: questionTypeList.map((item) => item.type)
}) })
const getNewQuestion = ({ type }) => {
const fields = questionDataList.value.map((item) => item.field)
const newQuestion = getQuestionByType(type, fields)
newQuestion.title = newQuestion.title = `标题${newQuestionIndex.value + 1}`
if (type === QUESTION_TYPE.VOTE) {
newQuestion.innerType = QUESTION_TYPE.RADIO
}
return newQuestion
}
const onQuestionType = ({ type }) => { const onQuestionType = ({ type }) => {
const newQuestion = getNewQuestion({ type }) const newQuestion = createNewQuestion({ type })
addQuestion({ question: newQuestion, index: newQuestionIndex.value }) addQuestion({ question: newQuestion, index: newQuestionIndex.value })
setCurrentEditOne(newQuestionIndex.value) setCurrentEditOne(newQuestionIndex.value)
} }

View File

@ -1,23 +1,18 @@
<template> <template>
<div class="main-operation"> <div class="main-operation">
<div class="pagination-wrapper">
<PagingWrapper :readonly="true" />
</div>
<div class="operation-wrapper"> <div class="operation-wrapper">
<div class="box" ref="box"> <div class="box" ref="box">
<div class="mask"></div> <div class="mask"></div>
<HeaderContent :bannerConf="bannerConf" :readonly="false" /> <HeaderContent v-if="pagingEditOne==1" :bannerConf="bannerConf" :readonly="false" />
<div class="content"> <div class="content">
<MainTitle :isSelected="false" :bannerConf="bannerConf" :readonly="false" /> <MainTitle v-if="pagingEditOne==1" :isSelected="false" :bannerConf="bannerConf" :readonly="false" />
<MaterialGroup :questionDataList="questionDataList" ref="MaterialGroup" /> <MaterialGroup :questionDataList="pagingQuestionData" ref="MaterialGroup" />
<SubmitButton <SubmitButton :submit-conf="submitConf" :skin-conf="skinConf" :readonly="false"
:submit-conf="submitConf" :is-selected="currentEditOne === 'submit'" :is-finally-page="isFinallyPage" />
:skin-conf="skinConf" <LogoIcon :logo-conf="bottomConf" :readonly="false" :is-selected="currentEditOne === 'logo'" />
:readonly="false"
:is-selected="currentEditOne === 'submit'"
/>
<LogoIcon
:logo-conf="bottomConf"
:readonly="false"
:is-selected="currentEditOne === 'logo'"
/>
</div> </div>
</div> </div>
</div> </div>
@ -26,6 +21,7 @@
<script> <script>
import { defineComponent, toRefs } 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 PagingWrapper from '@/management/pages/edit/components/PagingWrapper.vue'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { useEditStore } from '@/management/stores/edit' import { useEditStore } from '@/management/stores/edit'
import communalLoader from '@materials/communals/communalLoader.js' import communalLoader from '@materials/communals/communalLoader.js'
@ -38,6 +34,7 @@ const LogoIcon = () => communalLoader.loadComponent('LogoIcon')
export default defineComponent({ export default defineComponent({
components: { components: {
MaterialGroup, MaterialGroup,
PagingWrapper,
HeaderContent: HeaderContent(), HeaderContent: HeaderContent(),
MainTitle: MainTitle(), MainTitle: MainTitle(),
SubmitButton: SubmitButton(), SubmitButton: SubmitButton(),
@ -45,7 +42,7 @@ export default defineComponent({
}, },
setup() { setup() {
const editStore = useEditStore() const editStore = useEditStore()
const { questionDataList, currentEditOne, currentEditKey } = storeToRefs(editStore) const { pagingQuestionData, currentEditOne, currentEditKey,isFinallyPage,pagingEditOne } = storeToRefs(editStore)
const { schema } = editStore const { schema } = editStore
const { bannerConf, submitConf, skinConf, bottomConf } = toRefs(schema) const { bannerConf, submitConf, skinConf, bottomConf } = toRefs(schema)
@ -54,9 +51,11 @@ export default defineComponent({
submitConf, submitConf,
bottomConf, bottomConf,
skinConf, skinConf,
questionDataList, pagingQuestionData,
currentEditOne, currentEditOne,
currentEditKey currentEditKey,
isFinallyPage,
pagingEditOne
} }
}, },
watch: { watch: {
@ -92,6 +91,12 @@ export default defineComponent({
background-color: #f6f7f9; background-color: #f6f7f9;
} }
.pagination-wrapper {
position: relative;
top: 50px;
width: 90%;
}
.toolbar { .toolbar {
width: 100%; width: 100%;
height: 38px; height: 38px;

View File

@ -1,6 +1,9 @@
import { type Ref, ref, reactive, toRef, computed } from 'vue' import { type Ref, ref, reactive, toRef, computed } from 'vue'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { merge as _merge, cloneDeep as _cloneDeep, set as _set } from 'lodash-es' import { merge as _merge, cloneDeep as _cloneDeep, set as _set,isNumber as _isNumber } from 'lodash-es'
import { QUESTION_TYPE } from '@/common/typeEnum'
import { getQuestionByType } from '@/management/utils/index'
import { filterQuestionPreviewData } from '@/management/utils/index'
import { getSurveyById } from '@/management/api/survey' import { getSurveyById } from '@/management/api/survey'
import { getNewField } from '@/management/utils' import { getNewField } from '@/management/utils'
@ -71,8 +74,10 @@ function useInitializeSchema(surveyId: Ref<string>) {
link: '' link: ''
}, },
questionDataList: [], questionDataList: [],
pagingEditOne: 1,
pagingConf:[], // 分页逻辑
logicConf: { logicConf: {
showLogicConf: [] showLogicConf: [],
} }
}) })
@ -84,7 +89,9 @@ function useInitializeSchema(surveyId: Ref<string>) {
schema.baseConf = _merge({}, schema.baseConf, codeData.baseConf) schema.baseConf = _merge({}, schema.baseConf, codeData.baseConf)
schema.submitConf = _merge({}, schema.submitConf, codeData.submitConf) schema.submitConf = _merge({}, schema.submitConf, codeData.submitConf)
schema.questionDataList = codeData.questionDataList || [] schema.questionDataList = codeData.questionDataList || []
schema.logicConf = codeData.logicConf schema.logicConf = codeData.logicConf;
schema.pagingEditOne = 1;
schema.pagingConf = codeData.pagingConf;
} }
async function getSchemaFromRemote() { async function getSchemaFromRemote() {
@ -92,15 +99,19 @@ function useInitializeSchema(surveyId: Ref<string>) {
if (res.code === 200) { if (res.code === 200) {
const metaData = res.data.surveyMetaRes const metaData = res.data.surveyMetaRes
document.title = metaData.title document.title = metaData.title
const data = res.data.surveyConfRes.code
const { const {
bannerConf, bannerConf,
bottomConf, bottomConf,
skinConf, skinConf,
baseConf, baseConf,
submitConf, submitConf,
dataConf, dataConf,
logicConf = {} logicConf = {}
} = res.data.surveyConfRes.code } = data
if (!data.pagingConf || data.pagingConf.length === 0) {
data.pagingConf = [dataConf.dataList.length]
}
initSchema({ initSchema({
metaData, metaData,
codeData: { codeData: {
@ -110,6 +121,7 @@ function useInitializeSchema(surveyId: Ref<string>) {
baseConf, baseConf,
submitConf, submitConf,
questionDataList: dataConf.dataList, questionDataList: dataConf.dataList,
pagingConf: data.pagingConf,
logicConf logicConf
} }
}) })
@ -125,7 +137,7 @@ function useInitializeSchema(surveyId: Ref<string>) {
} }
} }
function useQuestionDataListOperations(questionDataList: Ref<any[]>, updateTime: () => void) { function useQuestionDataListOperations(questionDataList: Ref<any[]>, updateTime: () => void,pagingOperations:(type:string)=>void) {
function copyQuestion({ index }: { index: number }) { function copyQuestion({ index }: { index: number }) {
const newQuestion = _cloneDeep(questionDataList.value[index]) const newQuestion = _cloneDeep(questionDataList.value[index])
newQuestion.field = getNewField(questionDataList.value.map((item) => item.field)) newQuestion.field = getNewField(questionDataList.value.map((item) => item.field))
@ -134,10 +146,12 @@ function useQuestionDataListOperations(questionDataList: Ref<any[]>, updateTime:
function addQuestion({ question, index }: { question: any; index: number }) { function addQuestion({ question, index }: { question: any; index: number }) {
questionDataList.value.splice(index, 0, question) questionDataList.value.splice(index, 0, question)
pagingOperations('add')
updateTime() updateTime()
} }
function deleteQuestion({ index }: { index: number }) { function deleteQuestion({ index }: { index: number }) {
pagingOperations('remove')
questionDataList.value.splice(index, 1) questionDataList.value.splice(index, 1)
updateTime() updateTime()
} }
@ -266,6 +280,138 @@ function useCurrentEdit({
changeCurrentEditStatus changeCurrentEditStatus
} }
} }
function usePagingEdit({
schema,
questionDataList
}: {
schema: any,
questionDataList: Ref<any[]>
},updateTime: () => void) {
const pagingConf = computed(() => schema.pagingConf)
const pagingEditOne = computed(() => schema.pagingEditOne)
const isFinallyPage = computed(() => {
return pagingEditOne.value === pagingConf.value.length;
})
const pagingCount = computed(() => pagingConf.value.length || 0)
const pagingQuestionData = computed(() => {
return getPagingQuestionData(pagingEditOne.value)
})
const getPagingQuestionData = (index:number) => {
const { startIndex, endIndex } = getSorter(index)
return filterQuestionPreviewData(questionDataList.value).slice(startIndex,endIndex)
}
const getSorter = (index?:number) => {
let startIndex = 0;
const newPagingEditOne = index || pagingEditOne.value;
const endIndex = pagingConf.value[newPagingEditOne - 1];
for (let index = 0; index < pagingConf.value.length; index++) {
const item = pagingConf.value[index];
if ((newPagingEditOne - 1) == index) {
break;
}
startIndex+=item
}
return {
startIndex,
endIndex:startIndex + endIndex
}
}
const addPaging = () => {
schema.pagingConf.push(1)
}
const updatePagingEditOne = (index: number) => {
schema.pagingEditOne = index;
}
const deletePaging = (index: number) => {
if (pagingConf.value.length <= 1) return
const { startIndex, endIndex } = getSorter(index)
const newQuestion = _cloneDeep(questionDataList.value)
newQuestion.splice(startIndex, endIndex-startIndex)
updatePagingEditOne(1);
schema.pagingConf.splice(index-1, 1)
questionDataList.value = newQuestion
updateTime();
}
const swapArrayRanges = (index:number,range:number) => {
const { startIndex:start1,endIndex:end1 } = getSorter(index)
const { startIndex:start2,endIndex:end2 } = getSorter(range)
const newQuestion = _cloneDeep(questionDataList.value)
const range1 = newQuestion.slice(start1, end1);
const range2 = newQuestion.slice(start2, end2 );
newQuestion.splice(start1, range1.length, ...range2);
newQuestion.splice(start2, range2.length, ...range1);
questionDataList.value = newQuestion
const rangeCount = schema.pagingConf[range-1]
schema.pagingConf[range-1] = schema.pagingConf[index-1]
schema.pagingConf[index - 1] = rangeCount;
updateTime();
}
const copyPaging = (index: number) => {
const newQuestionList = _cloneDeep(getPagingQuestionData(index))
newQuestionList.forEach((item) => {
item.field = getNewField(questionDataList.value.map((item) => item.field))
})
schema.pagingConf.splice(index, 0, newQuestionList.length)
const { endIndex } = getSorter(index)
questionDataList.value.splice(endIndex, 0, ...newQuestionList)
updateTime();
}
const pagingOperations = (type:string) => {
const count = pagingConf.value[pagingEditOne.value - 1];
if (type == 'add') {
if (count!=undefined) {
schema.pagingConf[pagingEditOne.value - 1] = count + 1; ;
}
return
}
if (type == 'remove') {
if (count) {
schema.pagingConf[pagingEditOne.value - 1] = count - 1;
}
}
}
const setPaging = (data: Array<number>) => {
for (let index = 0; index < pagingConf.value.length; index++) {
const newIndex = data[index];
const oldIndex = pagingConf.value[index];
if (newIndex != oldIndex) {
schema.pagingConf[index] = newIndex;
}
}
}
return {
pagingEditOne,
pagingConf,
isFinallyPage,
pagingCount,
pagingQuestionData,
getSorter,
updatePagingEditOne,
deletePaging,
addPaging,
copyPaging,
getPagingQuestionData,
pagingOperations,
swapArrayRanges,
setPaging
}
}
type IBannerItem = { type IBannerItem = {
name: string name: string
@ -325,11 +471,72 @@ export const useEditStore = defineStore('edit', () => {
schemaUpdateTime.value = Date.now() schemaUpdateTime.value = Date.now()
} }
const { copyQuestion, addQuestion, deleteQuestion, moveQuestion } = useQuestionDataListOperations( const { pagingEditOne, pagingConf, isFinallyPage, pagingCount, pagingQuestionData, getSorter,
questionDataList, updatePagingEditOne, deletePaging,pagingOperations,addPaging,getPagingQuestionData,copyPaging,swapArrayRanges,setPaging }= usePagingEdit({ schema, questionDataList },updateTime)
updateTime
const { copyQuestion, addQuestion, deleteQuestion, moveQuestion} = useQuestionDataListOperations(
questionDataList,
updateTime,
pagingOperations
) )
function moveQuestionDataList(data: any) {
const { startIndex, endIndex } = getSorter()
const newData = [...questionDataList.value.slice(0, startIndex), ...data, ...questionDataList.value.slice(endIndex)];
const countTotal:number = (schema.pagingConf as Array<number>).reduce((v:number, i:number) => v + i)
if (countTotal != newData.length) {
schema.pagingConf[pagingEditOne.value - 1] = schema.pagingConf[pagingEditOne.value - 1] + 1 as never;
}
setQuestionDataList(newData)
}
const compareQuestionSeq = (val:Array<any>) => {
const newSeq: Array<string> = [];
const oldSeq: Array<string> = [];
let status = false;
val.map(v => {
newSeq.push(v.field)
});
(questionDataList.value as Array<any>).map(v => {
oldSeq.push(v.field)
})
for (let index = 0; index < newSeq.length; index++) {
if (newSeq[index] !== oldSeq[index]) {
status = true;
break;
}
}
if (status) {
setQuestionDataList(val);
}
}
const newQuestionIndex = computed(() => {
if (_isNumber(currentEditOne.value)) {
return currentEditOne.value + 1
} else {
const pagingConf = schema.pagingConf
const questCount = pagingConf[schema.pagingEditOne - 1];
const { startIndex,endIndex } = getSorter();
if (!questCount) {
return startIndex
}
return endIndex
}
})
const createNewQuestion = ({type}:{type:QUESTION_TYPE}) => {
const fields = questionDataList.value.map((item:any) => item.field)
const newQuestion = getQuestionByType(type, fields)
newQuestion.title = newQuestion.title = `标题${newQuestionIndex.value + 1}`
if (type === QUESTION_TYPE.VOTE) {
newQuestion.innerType = QUESTION_TYPE.RADIO
}
return newQuestion
}
function changeSchema({ key, value }: { key: string; value: any }) { function changeSchema({ key, value }: { key: string; value: any }) {
_set(schema, key, value) _set(schema, key, value)
updateTime() updateTime()
@ -354,12 +561,27 @@ export const useEditStore = defineStore('edit', () => {
currentEditKey, currentEditKey,
currentEditStatus, currentEditStatus,
currentEditMeta, currentEditMeta,
newQuestionIndex,
setCurrentEditOne, setCurrentEditOne,
changeCurrentEditStatus, changeCurrentEditStatus,
pagingEditOne,
pagingConf,
isFinallyPage,
pagingCount,
pagingQuestionData,
getSorter,
updatePagingEditOne,
deletePaging,
addPaging,
getPagingQuestionData,
copyPaging,
swapArrayRanges,
setPaging,
schemaUpdateTime, schemaUpdateTime,
schema, schema,
questionDataList, questionDataList,
setQuestionDataList, setQuestionDataList,
moveQuestionDataList,
init, init,
initSchema, initSchema,
getSchemaFromRemote, getSchemaFromRemote,
@ -367,7 +589,9 @@ export const useEditStore = defineStore('edit', () => {
addQuestion, addQuestion,
deleteQuestion, deleteQuestion,
moveQuestion, moveQuestion,
createNewQuestion,
changeSchema, changeSchema,
changeThemePreset changeThemePreset,
compareQuestionSeq
} }
}) })

View File

@ -10,6 +10,7 @@ export default defineComponent({
type: Object, type: Object,
default: () => ({}) default: () => ({})
}, },
isFinallyPage: Boolean,
readonly: Boolean, readonly: Boolean,
validate: Function, validate: Function,
renderData: Array renderData: Array
@ -41,11 +42,11 @@ export default defineComponent({
} }
}, },
render() { render() {
const { submitConf } = this.props const { submitConf,isFinallyPage } = this.props
return ( return (
<div class={['submit-warp', 'preview-submit_wrapper']} onClick={this.handleClick}> <div class={['submit-warp', 'preview-submit_wrapper']} onClick={this.handleClick}>
<button class="submit-btn" type="primary" onClick={this.submit}> <button class="submit-btn" type="primary" onClick={this.submit}>
{submitConf.submitTitle} {isFinallyPage ? submitConf.submitTitle : '下一页'}
</button> </button>
</div> </div>
) )

View File

@ -16,7 +16,7 @@ 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
const { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf, logicConf } = const { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf, logicConf,pagingConf } =
data.code data.code
const questionData = { const questionData = {
bannerConf, bannerConf,
@ -24,7 +24,12 @@ const loadData = (res: any, surveyPath: string) => {
bottomConf, bottomConf,
dataConf, dataConf,
skinConf, skinConf,
submitConf submitConf,
pagingConf
}
if(!pagingConf || pagingConf?.length == 0){
questionData.pagingConf = [dataConf.dataList.length]
} }
document.title = data.title document.title = data.title

View File

@ -2,14 +2,15 @@
<div class="index"> <div class="index">
<ProgressBar /> <ProgressBar />
<div class="wrapper" ref="boxRef"> <div class="wrapper" ref="boxRef">
<HeaderContent :bannerConf="bannerConf" :readonly="true" /> <HeaderContent v-if="pagingIndex==1" :bannerConf="bannerConf" :readonly="true" />
<div class="content"> <div class="content">
<MainTitle :bannerConf="bannerConf" :readonly="true"></MainTitle> <MainTitle v-if="pagingIndex==1" :bannerConf="bannerConf" :readonly="true"></MainTitle>
<MainRenderer ref="mainRef"></MainRenderer> <MainRenderer ref="mainRef"></MainRenderer>
<SubmitButton <SubmitButton
:validate="validate" :validate="validate"
:submitConf="submitConf" :submitConf="submitConf"
:readonly="true" :readonly="true"
:isFinallyPage="isFinallyPage"
:renderData="renderData" :renderData="renderData"
@submit="handleSubmit" @submit="handleSubmit"
></SubmitButton> ></SubmitButton>
@ -61,14 +62,11 @@ const router = useRouter();
const surveyStore = useSurveyStore(); const surveyStore = useSurveyStore();
const questionStore = useQuestionStore(); const questionStore = useQuestionStore();
const renderData = computed(() => questionStore.renderData); const renderData = computed(() => questionStore.renderData)
const { const isFinallyPage = computed(() => questionStore.isFinallyPage)
bannerConf, const pagingIndex = computed(() => questionStore.pagingIndex)
submitConf, const { bannerConf, submitConf, bottomConf: logoConf, whiteData } = storeToRefs(surveyStore)
bottomConf: logoConf, const surveyPath = computed(() => surveyStore.surveyPath || '')
whiteData,
} = storeToRefs(surveyStore);
const surveyPath = computed(() => surveyStore.surveyPath || "");
const validate = (cbk: (v: boolean) => void) => { const validate = (cbk: (v: boolean) => void) => {
const index = 0; const index = 0;
@ -126,9 +124,12 @@ const submitSurver = async () => {
}; };
const handleSubmit = () => { const handleSubmit = () => {
const confirmAgain = (surveyStore.submitConf as any).confirmAgain; const confirmAgain = (surveyStore.submitConf as any).confirmAgain
const { again_text, is_again } = confirmAgain; const { again_text, is_again } = confirmAgain
if (!isFinallyPage.value) {
questionStore.addPagingIndex();
return
}
if (is_again) { if (is_again) {
confirm({ confirm({
title: again_text, title: again_text,

View File

@ -10,9 +10,10 @@ export const useQuestionStore = defineStore('question', () => {
const voteMap = ref({}) const voteMap = ref({})
const questionData = ref(null) const questionData = ref(null)
const questionSeq = ref([]) // 题目的顺序,因为可能会有分页的情况,所以是一个二维数组[[qid1, qid2], [qid3,qid4]] const questionSeq = ref([]) // 题目的顺序,因为可能会有分页的情况,所以是一个二维数组[[qid1, qid2], [qid3,qid4]]
const pagingIndex = ref(1) // 当前分页的索引
// 题目列表 // 题目列表
const renderData = computed(() => { const questionList = computed(() => {
let index = 1 let index = 1
return ( return (
questionSeq.value && questionSeq.value &&
@ -38,6 +39,41 @@ export const useQuestionStore = defineStore('question', () => {
) )
}) })
const renderData = computed(() => {
const { startIndex, endIndex } = getSorter()
const data = questionList.value[0];
if(!data || !Array.isArray(data) ||data.length === 0) return []
return [data.slice(startIndex,endIndex)]
})
const isFinallyPage = computed(() => {
const surveyStore = useSurveyStore()
return pagingIndex.value === surveyStore.pagingConf.length;
})
const addPagingIndex = () => {
pagingIndex.value++
}
const getSorter = () => {
let startIndex = 0;
const surveyStore = useSurveyStore()
const newPagingEditOne = pagingIndex.value;
const endIndex = surveyStore.pagingConf[newPagingEditOne - 1];
for (let index = 0; index < surveyStore.pagingConf.length; index++) {
const item = surveyStore.pagingConf[index];
if ((newPagingEditOne - 1) == index) {
break;
}
startIndex+=item
}
return {
startIndex,
endIndex:startIndex + endIndex
}
}
const setQuestionData = (data) => { const setQuestionData = (data) => {
questionData.value = data questionData.value = data
} }
@ -146,6 +182,9 @@ export const useQuestionStore = defineStore('question', () => {
questionData, questionData,
questionSeq, questionSeq,
renderData, renderData,
isFinallyPage,
pagingIndex,
addPagingIndex,
setQuestionData, setQuestionData,
changeSelectMoreData, changeSelectMoreData,
setQuestionSeq, setQuestionSeq,

View File

@ -38,6 +38,7 @@ export const useSurveyStore = defineStore('survey', () => {
const submitConf = ref({}) const submitConf = ref({})
const formValues = ref({}) const formValues = ref({})
const whiteData = ref({}) const whiteData = ref({})
const pagingConf = ref([])
const router = useRouter() const router = useRouter()
const questionStore = useQuestionStore() const questionStore = useQuestionStore()
@ -127,10 +128,10 @@ export const useSurveyStore = defineStore('survey', () => {
'dataConf', 'dataConf',
'skinConf', 'skinConf',
'submitConf', 'submitConf',
'whiteData' 'whiteData',
"pagingConf"
]) ])
) )
questionStore.questionData = questionData questionStore.questionData = questionData
questionStore.questionSeq = questionSeq questionStore.questionSeq = questionSeq
@ -144,7 +145,7 @@ export const useSurveyStore = defineStore('survey', () => {
submitConf.value = option.submitConf submitConf.value = option.submitConf
formValues.value = _formValues formValues.value = _formValues
whiteData.value = option.whiteData whiteData.value = option.whiteData
pagingConf.value = option.pagingConf
// 获取已投票数据 // 获取已投票数据
questionStore.initVoteData() questionStore.initVoteData()
} }
@ -171,6 +172,7 @@ export const useSurveyStore = defineStore('survey', () => {
submitConf, submitConf,
formValues, formValues,
whiteData, whiteData,
pagingConf,
initSurvey, initSurvey,
changeData, changeData,