feat: css 处理 增加font 增加 选择题目

This commit is contained in:
陈昱达
2025-03-03 09:20:13 +08:00
parent 66756b4cf2
commit 1151a89aa9
27 changed files with 1314 additions and 108 deletions

1
.d.ts vendored
View File

@@ -1,3 +1,4 @@
// amfe-flexible.d.ts
declare module 'amfe-flexible'

8
components.d.ts vendored
View File

@@ -13,5 +13,13 @@ declare module 'vue' {
ElMain: typeof import('element-plus/es')['ElMain']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
VanButton: typeof import('vant/es')['Button']
VanCell: typeof import('vant/es')['Cell']
VanCellGroup: typeof import('vant/es')['CellGroup']
VanCheckbox: typeof import('vant/es')['Checkbox']
VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
VanDivider: typeof import('vant/es')['Divider']
VanField: typeof import('vant/es')['Field']
VanIcon: typeof import('vant/es')['Icon']
}
}

5
env.d.ts vendored
View File

@@ -1 +1,6 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import {DefineComponent} from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}

40
package-lock.json generated
View File

@@ -13,6 +13,8 @@
"lodash": "^4.17.21",
"pinia": "^2.1.7",
"sass": "^1.77.8",
"sortablejs": "^1.15.6",
"uuid": "^11.1.0",
"vant": "^4.9.17",
"vue": "^3.4.29",
"vue-router": "^4.3.3"
@@ -21,6 +23,7 @@
"@tsconfig/node20": "^20.1.4",
"@types/node": "^20.14.5",
"@types/postcss-pxtorem": "^6.1.0",
"@vant/auto-import-resolver": "^1.3.0",
"@vitejs/plugin-vue": "^5.0.5",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"@vue/tsconfig": "^0.5.1",
@@ -30,8 +33,8 @@
"npm-run-all2": "^6.2.0",
"postcss-pxtorem": "^6.1.0",
"typescript": "~5.4.0",
"unplugin-auto-import": "^0.18.2",
"unplugin-vue-components": "^0.27.3",
"unplugin-auto-import": "^0.18.6",
"unplugin-vue-components": "^0.27.5",
"vite": "^5.3.1",
"vue-tsc": "^2.0.21"
}
@@ -1889,6 +1892,12 @@
"dev": true,
"peer": true
},
"node_modules/@vant/auto-import-resolver": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/@vant/auto-import-resolver/-/auto-import-resolver-1.3.0.tgz",
"integrity": "sha512-lJyWtCyFizR4bHZvMiNMF3w+WTFTUWAvka1eqTnPK9ticUcKTCOx6qEmHcm8JPb3g1t3GaD2W3MnHkBp/nHamw==",
"dev": true
},
"node_modules/@vant/popperjs": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/@vant/popperjs/-/popperjs-1.3.0.tgz",
@@ -4401,18 +4410,10 @@
}
},
"node_modules/@yl/yili-fe-lint-config/node_modules/typescript": {
"version": "5.7.3",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.7.3.tgz",
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
"dev": true,
"inBundle": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
"inBundle": true
},
"node_modules/@yl/yili-fe-lint-config/node_modules/uri-js": {
"version": "4.4.1",
@@ -9195,6 +9196,11 @@
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
}
},
"node_modules/sortablejs": {
"version": "1.15.6",
"resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.6.tgz",
"integrity": "sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A=="
},
"node_modules/source-map-js": {
"version": "1.2.1",
"license": "BSD-3-Clause",
@@ -10292,6 +10298,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/uuid": {
"version": "11.1.0",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/esm/bin/uuid"
}
},
"node_modules/vant": {
"version": "4.9.17",
"resolved": "https://registry.npmmirror.com/vant/-/vant-4.9.17.tgz",

View File

@@ -18,6 +18,8 @@
"lodash": "^4.17.21",
"pinia": "^2.1.7",
"sass": "^1.77.8",
"sortablejs": "^1.15.6",
"uuid": "^11.1.0",
"vant": "^4.9.17",
"vue": "^3.4.29",
"vue-router": "^4.3.3"
@@ -26,6 +28,7 @@
"@tsconfig/node20": "^20.1.4",
"@types/node": "^20.14.5",
"@types/postcss-pxtorem": "^6.1.0",
"@vant/auto-import-resolver": "^1.3.0",
"@vitejs/plugin-vue": "^5.0.5",
"@vitejs/plugin-vue-jsx": "^4.0.0",
"@vue/tsconfig": "^0.5.1",
@@ -35,8 +38,8 @@
"npm-run-all2": "^6.2.0",
"postcss-pxtorem": "^6.1.0",
"typescript": "~5.4.0",
"unplugin-auto-import": "^0.18.2",
"unplugin-vue-components": "^0.27.3",
"unplugin-auto-import": "^0.18.6",
"unplugin-vue-components": "^0.27.5",
"vite": "^5.3.1",
"vue-tsc": "^2.0.21"
}

View File

@@ -1,4 +1,5 @@
@import './base.css';
@import '../font/iconfont.css';
a,
.green {
@@ -13,3 +14,19 @@ a,
background-color: hsla(160deg, 100%, 37%, 0.2);
}
}
.container {
font-size: 12px;
}
.flex {
display: flex;
}
.align-center {
align-items: center;
}
.flex-start {
justify-content: flex-start;
}

541
src/fonts/demo.css Normal file
View File

@@ -0,0 +1,541 @@
/* Logo 字体 */
@font-face {
font-family: 'iconfont logo';
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-style: normal;
font-size: 160px;
font-family: 'iconfont logo';
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
color: #666;
line-height: 42px;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
position: relative;
z-index: 1;
width: 100px;
height: 40px;
margin-bottom: -1px;
border-bottom: 2px solid transparent;
color: #666;
font-size: 16px;
line-height: 40px;
text-align: center;
cursor: pointer;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
width: 960px;
margin: 0 auto;
padding: 30px 100px;
}
.main .logo {
overflow: hidden;
height: 110px;
margin-top: -50px;
margin-bottom: 30px;
color: #333;
line-height: 1;
text-align: left;
*zoom: 1;
}
.main .logo a {
color: #333;
font-size: 160px;
}
.helps {
margin-top: 40px;
}
.helps pre {
overflow: auto;
margin: 10px 0;
padding: 20px;
border: solid 1px #e7e1cd;
background-color: #fffdef;
}
.icon_lists {
overflow: hidden;
width: 100% !important;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-right: 20px;
margin-bottom: 10px;
list-style: none !important;
text-align: center;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
margin: 10px auto;
color: #333;
font-size: 42px;
line-height: 100px;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentcolor;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
margin-bottom: 24px;
color: #404040;
font-weight: 500;
line-height: 40px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
clear: both;
margin: 1.6em 0 0.6em;
color: #404040;
font-weight: 500;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
clear: both;
height: 1px;
margin: 16px 0;
border: 0;
background: #e9e9e9;
}
.markdown p {
margin: 1em 0;
}
.markdown > p,
.markdown > blockquote,
.markdown > .highlight,
.markdown > ol,
.markdown > ul {
width: 80%;
}
.markdown ul > li {
list-style: circle;
}
.markdown > ul li,
.markdown blockquote ul > li {
margin-left: 20px;
padding-left: 4px;
}
.markdown > ul li p,
.markdown > ol li p {
margin: 0.6em 0;
}
.markdown ol > li {
list-style: decimal;
}
.markdown > ol li,
.markdown blockquote ol > li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
border-radius: 3px;
background: #eee;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown > table {
width: 95%;
margin-bottom: 24px;
border: 1px solid #e9e9e9;
border-spacing: 0;
border-collapse: collapse;
empty-cells: show;
}
.markdown > table th {
color: #333;
font-weight: 600;
white-space: nowrap;
}
.markdown > table th,
.markdown > table td {
padding: 8px 16px;
border: 1px solid #e9e9e9;
text-align: left;
}
.markdown > table th {
background: #F7F7F7;
}
.markdown blockquote {
margin: 1em 0;
padding-left: 0.8em;
border-left: 4px solid #e9e9e9;
color: #999;
font-size: 90%;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
margin-left: 8px;
opacity: 0;
transition: opacity 0.3s ease;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
display: inline-block;
opacity: 1;
}
.markdown > br,
.markdown > p > br {
clear: both;
}
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: white;
color: #333;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
background-color: #eaffea;
color: #55a532;
}
.hljs-deletion {
background-color: #ffecec;
color: #bd2c00;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*='language-'],
pre[class*='language-'] {
background: none;
color: black;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
line-height: 1.5;
text-align: left;
text-shadow: 0 1px white;
white-space: pre;
word-spacing: normal;
word-wrap: normal;
word-break: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*='language-']::-moz-selection,
pre[class*='language-'] ::-moz-selection,
code[class*='language-']::-moz-selection,
code[class*='language-'] ::-moz-selection {
background: #b3d4fc;
text-shadow: none;
}
pre[class*='language-']::selection,
pre[class*='language-'] ::selection,
code[class*='language-']::selection,
code[class*='language-'] ::selection {
background: #b3d4fc;
text-shadow: none;
}
@media print {
code[class*='language-'],
pre[class*='language-'] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*='language-'] {
overflow: auto;
margin: .5em 0;
padding: 1em;
}
:not(pre) > code[class*='language-'],
pre[class*='language-'] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*='language-'] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
background: hsla(0deg, 0%, 100%, .5);
color: #9a6e3a;
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

29
src/fonts/iconfont.css Normal file
View File

@@ -0,0 +1,29 @@
@font-face {
font-family: iconfont; /* Project id */
src: url('iconfont.ttf?t=1740720237938') format('truetype');
}
.iconfont {
font-style: normal;
font-size: 16px;
font-family: iconfont !important;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-edit2::before {
content: '\e630';
}
.icon-copy::before {
content: '\e632';
}
.icon-delete::before {
content: '\e63f';
}
.icon-sort::before {
content: '\e6a0';
}

18
src/fonts/iconfont.js Normal file
View File

@@ -0,0 +1,18 @@
const e = 'someValue';
const a = 'anotherValue';
const i = 'yetAnotherValue';
const n = 'andAnotherValue';
const o = 'oneMoreValue';
const t = 'lastOne';
function someFunction() {
const r = document.createElement('style');
r.type = 'text/css';
document.head.appendChild(r);
if (r.styleSheet) {
r.styleSheet.cssText = e + a + i + n + o + t;
} else {
r.appendChild(document.createTextNode(e + a + i + n + o + t));
}
}
someFunction();

37
src/fonts/iconfont.json Normal file
View File

@@ -0,0 +1,37 @@
{
"id": "",
"name": "",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "1160114",
"name": "edit-2",
"font_class": "edit2",
"unicode": "e630",
"unicode_decimal": 58928
},
{
"icon_id": "1160116",
"name": "copy",
"font_class": "copy",
"unicode": "e632",
"unicode_decimal": 58930
},
{
"icon_id": "1160129",
"name": "delete",
"font_class": "delete",
"unicode": "e63f",
"unicode_decimal": 58943
},
{
"icon_id": "1160227",
"name": "sort",
"font_class": "sort",
"unicode": "e6a0",
"unicode_decimal": 59040
}
]
}

BIN
src/fonts/iconfont.ttf Normal file

Binary file not shown.

View File

@@ -1,39 +1,20 @@
<template>
<div class="common-layout">
<el-container>
<el-header>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</el-header>
<el-main>
<RouterView />
</el-main>
</el-container>
</div>
</template>
<script setup>
import { RouterLink, RouterView } from 'vue-router';
import { RouterView } from 'vue-router';
</script>
<style scoped lang="scss">
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
line-height: 60px;
text-align: center;
}
.el-main {
height: calc(100vh - 60px);
.common-layout {
height: calc(100vh);
background-color: #E9EEF3;
color: #333;
text-align: center;
}
}
body > .el-container {
margin-bottom: 40px;
}
</style>

View File

@@ -1,19 +1,10 @@
import './assets/main.css';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
import 'amfe-flexible';
import Vant from 'vant'
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
const app = createApp(App);
app.use(Vant);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.use(createPinia());
app.use(router);
app.mount('#app');

View File

@@ -1,21 +1,27 @@
import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '../views/HomeView.vue';
// import HomeView from '../views/HomeView.vue';
import Design from '@/views/Design/Index.vue';
import QuestionEdit from '@/views/Design/QuestionEdit.vue';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
// {
// path: '/',
// name: 'home',
// component: HomeView
// },
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
path: '/design',
name: 'design',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
component: Design
},
{
path: '/design/edit',
name: 'questionEdit',
component: QuestionEdit
}
]
});

View File

@@ -0,0 +1,38 @@
export const A_COMMON_GET_QUESTIONINFO = 'A_COMMON_GET_QUESTIONINFO';
export const A_COMMON_SET_QUESTIONINFO = 'A_COMMON_SET_QUESTIONINFO';
export const A_COMMON_GET_QUESSAVEPARAM = 'A_COMMON_GET_QUESSAVEPARAM';
export const A_COMMON_SET_QUESSAVEPARAM = 'A_COMMON_SET_QUESSAVEPARAM';
export const A_COMMON_GET_ACTIVEQUESTION = 'A_COMMON_GET_ACTIVEQUESTION';
export const A_COMMON_SET_ACTIVEQUESTION = 'A_COMMON_SET_ACTIVEQUESTION';
export const A_COMMON_SET_BANK_LIST = 'A_COMMON_SET_BANK_LIST';
export const A_COMMON_GET_BANK_LIST = 'A_COMMON_GET_BANK_LIST';
export const A_COMMON_GET_WEBSOCKET = 'A_COMMON_GET_WEBSOCKET';
export const A_COMMON_SET_WEBSOCKET = 'A_COMMON_SET_WEBSOCKET';
export const A_COMMON_GET_TOKEN = 'A_COMMON_GET_TOKEN';
export const A_COMMON_CLEAR_TOKEN = 'A_COMMON_CLEAR_TOKEN';
export const M_COMMON_SET_TOKEN = 'M_COMMON_SET_TOKEN';
export const M_COMMON_GET_USERINFO = 'M_COMMON_GET_USERINFO';
export const M_COMMON_SET_USERINFO = 'M_COMMON_SET_USERINFO';
export const M_COMMON_SET_SURVEY_INFO = 'M_COMMON_SET_SURVEY_INFO';
export const M_COMMON_SET_SURVEY_STATUS = 'M_COMMON_SET_SURVEY_STATUS';
export const M_COMMON_SET_SURVEY_SCENEID = 'M_COMMON_SET_SURVEY_SCENEID';
export const M_COMMON_SET_SURVEY_SCENEPARENTID = 'M_COMMON_SET_SURVEY_SCENEPARENTID';
export const M_COMMON_SET_SURVEY_NAME = 'M_COMMON_SET_SURVEY_NAME';
export const M_COMMON_SET_TOKEN_UNAUTHORIZED = 'M_COMMON_SET_TOKEN_UNAUTHORIZED';
export const GET_FILE_TYPE = 'GET_FILE_TYPE';
export const M_COMMON_SET_THEME_INFO = 'M_COMMON_SET_THEME_INFO';
export const HEADER_IMG_INFO = 'HEADER_IMG_INFO';
export const DELETET_HEME_INFO_HEADIMGURL = 'DELETET_HEME_INFO_HEADIMGURL';
export const GUIDE_SELECT_NEED = 'GUIDE_SELECT_NEED';
export const GUIDE_OTHER_NEED = 'GUIDE_OTHER_NEED';
export const ANSWER_SESSION_ID = 'ANSWER_SESSION_ID';

View File

@@ -1,12 +1,21 @@
import { ref, computed } from 'vue';
import { defineStore } from 'pinia';
// src/stores/counter.ts
import { defineStore, storeToRefs } from 'pinia';
import { useCommonStore } from './modules/common';
// 类型安全的实现方式
const modulesFiles = import.meta.glob('./modules/**/*.ts', { eager: true });
const modules = Object.entries(modulesFiles).reduce(
(acc: { [key: string]: any }, [path, module]) => {
const moduleName = path.replace(/^\.\/modules\/(.*)\.\w+$/, '$1');
acc[moduleName] = (module as any).default;
return acc;
},
{}
);
export const useCounterStore = defineStore('counter', () => {
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
function increment() {
count.value += 1;
}
return { count, doubleCount, increment };
// 引入 common 模块
const commonStore = useCommonStore();
const { questionsInfo } = storeToRefs(commonStore);
// 返回需要暴露的内容
return { commonStore, questionsInfo, modules };
});

View File

@@ -0,0 +1,80 @@
// src/stores/modules/common.ts
import { defineStore } from 'pinia';
// 假设 uid 是一个生成唯一 ID 的函数
// 如果没有现成的 uid 函数,可以引入一个 UUID 库
// 或者使用其他 UUID 库
import { v4 as uuidv4 } from 'uuid';
export const useCommonStore = defineStore('common', {
state: () => ({
questionsInfo: {
// 初始化为 null 或者空对象 {}
questions: [
{
id: '8819cf74-70dc-4b7d-9153-857230e50dd3',
question_type: 1,
question_index: 1,
stem: '请选择您的年龄',
title: '',
options: [
[
{
id: '451adf64-07da-49ae-9b43-1bff91045dd8',
is_fixed: 0,
is_other: 0,
is_remove_other: 0,
option: '<p>18岁以下</p>',
option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' },
option_index: 1,
parent_id: 0,
type: 0,
cascade: [],
config: []
},
{ id: '9168a75d-a007-42b6-b746-b220d1c05c7a', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>18-25岁</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 2, parent_id: 0, type: 0, cascade: [], config: [] }, { id: 'a4458a57-9cdf-483d-aa1e-23d190f349b8', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>26-35岁</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 3, parent_id: 0, type: 0, cascade: [], config: [] }, { id: 'b15f6faf-cdc8-4d3b-8b91-feec6c191014', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>36-45岁</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 4, parent_id: 0, type: 0, cascade: [], config: [] }, { id: '45ddfb00-275c-4a70-a9ca-1f136cf33db4', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>46-55岁</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 5, parent_id: 0, type: 0, cascade: [], config: [] }, { id: '4fdf3d35-a204-42f2-8d33-705a4a6f63d4', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>56岁以上</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 6, parent_id: 0, type: 0, cascade: [], config: [] }]],
last_option_index: 6,
config:
{
is_required: 1,
quick_type: 10,
is_show: ['is_required', 'select_random', 'each_number'],
each_number: 1,
select_random: 0
},
associate: [],
question_code: '',
logic_config:
{ order: 0, type: 0, expect: '', stay_time: '' }
},
{
id: '8819cf74-70dc-4b7d-9153-857230e50d34', question_type: 1, question_index: 1, stem: '请选择您的年龄', title: '', options: [[{ id: '451adf64-07da-49ae-9b43-1bff91045dd8', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>18岁以下</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 1, parent_id: 0, type: 0, cascade: [], config: [] }, { id: '9168a75d-a007-42b6-b746-b220d1c05c7a', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>18-25岁</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 2, parent_id: 0, type: 0, cascade: [], config: [] }, { id: 'a4458a57-9cdf-483d-aa1e-23d190f349b8', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>26-35岁</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 3, parent_id: 0, type: 0, cascade: [], config: [] }, { id: 'b15f6faf-cdc8-4d3b-8b91-feec6c191014', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>36-45岁</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 4, parent_id: 0, type: 0, cascade: [], config: [] }, { id: '45ddfb00-275c-4a70-a9ca-1f136cf33db4', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>46-55岁</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 5, parent_id: 0, type: 0, cascade: [], config: [] }, { id: '4fdf3d35-a204-42f2-8d33-705a4a6f63d4', is_fixed: 0, is_other: 0, is_remove_other: 0, option: '<p>56岁以上</p>', option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' }, option_index: 6, parent_id: 0, type: 0, cascade: [], config: [] }]], last_option_index: 6, config: { is_required: 1, quick_type: 10, is_show: ['is_required', 'select_random', 'each_number'], each_number: 1, select_random: 0 }, associate: [], question_code: '', logic_config: { order: 0, type: 0, expect: '', stay_time: '' }
}]
}
}),
actions: {
async fetchQuestionInfo(questionInfo) {
try {
if (!questionInfo) return;
const info = JSON.parse(questionInfo);
if (info?.questions?.length) {
info.questions.forEach((page) => {
// 使用 UUID 生成唯一 ID
if (page?.page) {
page.pageId = page.pageId || uuidv4();
}
});
}
this.setQuestionInfo(info);
} catch (error) {
//未来扩展
}
},
setQuestionInfo(questionInfo) {
this.questionsInfo = questionInfo;
}
},
getters: {
getQuestionsInfo: (state) => state.questionsInfo
}
});

View File

@@ -1,15 +0,0 @@
<template>
<main>
<div>About</div>
<div>count: {{ count }}</div>
<div>doubleCount: {{ doubleCount }}</div>
</main>
</template>
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
const useCounter = useCounterStore();
const { count, doubleCount } = storeToRefs(useCounter);
</script>

View File

@@ -0,0 +1,54 @@
<template>
<div>
<draggable
v-model:data="questionInfo.questions"
item-key="id"
handle=".moverQues"
chosenClass="chosen"
animation="300"
:scroll="true"
>
<template #item="{ element, index }">
<choose-question
:element="element" :index="index" :chooseQuestionId="chooseQuestionId"
@get-choose-question-id="getChooseQuestionId"
>
<base-select v-if="element.question_type === 1" :element="element"></base-select>
</choose-question>
</template>
</draggable>
</div>
</template>
<script setup>
import { defineAsyncComponent, ref, onMounted } from 'vue';
import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
// 获取 Store 实例
const counterStore = useCounterStore();
// 修改访问方式
const store = storeToRefs(counterStore);
// import 组件
const Draggable = defineAsyncComponent(() => {
return import('./components/Draggable.vue');
});
const BaseSelect = defineAsyncComponent(() => {
return import('./components/Questions/BaseSelect.vue');
});
const ChooseQuestion = defineAsyncComponent(() => {
return import('./components/ChooseQuestion.vue');
});
const questionInfo = ref({ questions: [] });
const chooseQuestionId = ref('');
// 获取选中的题目的ID
const getChooseQuestionId = (questionItem) => {
chooseQuestionId.value = questionItem.id;
};
onMounted(() => {
questionInfo.value = store.questionsInfo.value;
});
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,40 @@
<template>
<div class="container question-edit-container">
<BaseSelectEdit v-if="questionElement.question_type === 1" :element="questionElement"></BaseSelectEdit>
</div>
</template>
<script setup>
import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
import { ref, onBeforeMount, defineAsyncComponent } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
// 组件
const BaseSelectEdit = defineAsyncComponent(() => import('@/views/Design/components/QuestionsEdit/BaseSelectEdit.vue'));
// 获取 Store 实例
const counterStore = useCounterStore();
// 修改访问方式
const { questionsInfo } = storeToRefs(counterStore);
const questionIndex = ref(-1);
const questionElement = ref({});
onBeforeMount(() => {
// 获取 query 参数
if (route.query.id) {
// 查找对应数据
questionElement.value = questionsInfo.value.questions.find((item, index) => {
if (item.id === route.query.id) {
questionIndex.value = index;
return item;
}
});
}
});
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,122 @@
<template>
<div class="choose-question-container">
<div :class="chooseQuestionId===element.id ? 'choose-question-active':''" @click="chooseItem">
<slot></slot>
<!-- 题目操作-->
<div v-if="chooseQuestionId===element.id" class="choose-question-active-container">
<div v-for="item in questionAction" :key="item.key" class="" :class="item.class?item.class:''" @click="itemAction(item)">
<i class="icon iconfont choose-question-active-container-icon" v-html="item.icon"></i>
<div class="choose-question-active-container-name">
{{ item.name }}
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
element: {
type: Object,
default: () => {
// 此方法目前为空,未来可能扩展
}
},
index: {
type: Number,
default: 0
},
chooseQuestionId: {
type: String,
default: '0'
}
});
const element = ref(props.element);
// emit 事件
// 选中题目后出现的操作
const questionAction = ref([
{
icon: '&#xe630;',
name: '编辑',
key: 'edit',
class: ''
},
{
icon: '&#xe632;',
name: '复制',
key: 'copy',
class: ''
},
{
icon: '&#xe6a0;',
name: '移动',
key: 'moveUp',
class: 'moverQues'
},
// {
// icon:'',
// name:'下移',
// key:'moveDown',
// class:''
// },
{
icon: '&#xe63f;',
name: '删除',
key: 'delete',
class: ''
}
]);
const emit = defineEmits(['getChooseQuestionId']);
// 选中题目
const chooseItem = () => {
// 使用从 defineProps 接收的 element 对象
emit('getChooseQuestionId', props.element);
};
const itemAction = (item) => {
switch (item.key) {
case 'edit':
break;
case 'copy':
break;
case 'moveUp':
break;
case 'moveDown':
break;
case 'delete':
break;
}
};
</script>
<style scoped lang="scss">
.choose-question-container {
padding: 5px;
& .choose-question-active {
outline: 1px dashed #409eff;
}
& .choose-question-active-container {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 20px;
color: #666;
font-size: 12px;
& .choose-question-active-container-icon {
font-size: 14px;
}
& .choose-question-active-container-name {
font-size: 12px;
}
}
}
</style>

View File

@@ -0,0 +1,104 @@
<template>
<div ref="draggable">
<div
v-for="(element, index) in data"
:key="element.id || element.pageId"
:data_id="element[itemKey] || index"
:data_index="index"
:data_key="element.id || element.pageId"
>
<slot name="item" :element="element" :index="index"></slot>
</div>
</div>
</template>
<script>
import Sortable from 'sortablejs';
import { onMounted, reactive, ref } from 'vue';
export default {
name: 'Draggable',
props: {
data: {
type: Array,
default: () => []
},
itemKey: {
type: String,
default: ''
},
handle: {
type: String,
default: ''
},
filter: {
type: String,
default: ''
},
chosenClass: {
type: String,
default: ''
},
animation: {
type: [String, Number],
default: 0
},
scroll: {
type: Boolean,
default: true
},
group: {
type: String,
default: ''
},
move: {
type: Function,
default: () => {
// 后续扩展
}
}
},
emits: ['update:data', 'end'],
setup(props, context) {
const sortable = ref(null);
const draggable = ref(null);
const copyData = reactive([]);
const options = {
handle: props.handle,
filter: props.filter,
chosenClass: props.chosenClass,
animation: props.animation,
scroll: props.scroll,
scrollSensitivity: 100,
forceFallback: true,
scrollSpeed: 30,
group: props.group,
onMove: (evt) => {
const index = evt.related.getAttribute('data_index');
evt.newIndex = Number(index);
return props.move(evt);
},
// 拖动结束
onEnd: (evt) => {
evt.stopPropagation();
const tempData = JSON.parse(JSON.stringify(props.data));
const oldEle = tempData[evt.oldIndex];
tempData.splice(evt.oldIndex, 1);
tempData.splice(evt.newIndex, 0, oldEle);
context.emit('update:data', tempData);
context.emit('end', evt);
}
};
onMounted(() => {
if (sortable.value) sortable.value.destory();
sortable.value = Sortable.create(draggable.value, options);
});
return {
copyData,
draggable
};
}
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,45 @@
<template>
<van-field
v-model="element.stem"
:label="element.stem"
:required="element.config.is_required=== 1"
label-align="top"
>
<template #input>
<van-checkbox-group>
<template v-for="item in element.options">
<van-checkbox
v-for="(it, index) in item"
:key="index"
:name="it.value"
:label="it.label"
:disabled="it.disabled"
icon-size="0.35rem"
>
<template #default>
<!-- {{it}}-->
<div v-html="it.option"></div>
</template>
</van-checkbox>
</template>
</van-checkbox-group>
</template>
</van-field>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
element: {
type: Object,
default: () => {
return {
stem: ''
};
}
}
});
const element = ref(props.element);
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,82 @@
<template>
<div class="container base-select-edit-container">
<van-field
label="题目"
required
placeholder="请输入题目内容"
label-align="top"
type="textarea"
/>
<div class="base-select-edit-divider">选项</div>
<van-cell-group v-for="(item,index) in props.element.options" :key="index" :name="item.value">
<van-field
v-for="(it,itIndex) in item" :key="itIndex" v-model="it.option" placeholder="输入选项"
label-width="0.25rem"
>
<template #label>
<van-icon name="clear" class="del" />
</template>
</van-field>
</van-cell-group>
<van-field label-width="0.25rem" class="flex align-center flex-start" readonly>
<template #label>
<van-icon name="add" class="plus" />
</template>
<template #input>
<div class="plus" @click="addOption">添加选项</div>
</template>
<template #button>
</template>
</van-field>
</div>
</template>
<script setup>
import { ref } from 'vue';
// 或者使用其他 UUID 库
import { v4 as uuidv4 } from 'uuid';
const props = defineProps({
element: {
type: Object,
default: () => {
return {};
}
}
});
const element = ref(props.element);
// 增加选项
const addOption = () => {
element.value.options[element.value.options.length - 1].push({
id: uuidv4(),
is_fixed: 0,
is_other: 0,
is_remove_other: 0,
option: '',
option_config: { image_url: [], title: '', instructions: [], option_type: 0, limit_right_content: '' },
option_index: 1,
parent_id: 0,
type: 0,
cascade: [],
config: []
});
};
</script>
<style scoped lang="scss">
.base-select-edit-container {
& .base-select-edit-divider {
margin: 5px;
text-align: left;
}
& .van-field__value {
border: 1px solid #ccc;
}
& .del {
color: red
}
& .plus {
color: #1989fa
}
}
</style>

View File

@@ -1,16 +0,0 @@
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
const useCounter = useCounterStore();
const { count, doubleCount } = storeToRefs(useCounter);
</script>
<template>
<main>
<div>Home</div>
<div>count: {{ count }}</div>
<div>doubleCount: {{ doubleCount }}</div>
<el-button @click="useCounter.increment()">累加</el-button>
</main>
</template>

View File

@@ -4,6 +4,7 @@
"env.d.ts",
"src/**/*",
"src/**/*.vue",
"src/**/**/*.vue",
"./auto-imports.d.ts",
"./components.d.ts",
"./amfe-flexible.d.ts"

View File

@@ -5,31 +5,38 @@ import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import {VantResolver} from 'unplugin-vue-components/resolvers';
import postCssPxToRem from 'postcss-pxtorem'
// https://vitejs.dev/config/
export default defineConfig({
server: {
host: '0.0.0.0', // 监听所有网络接口
port: 3000, // 你也可以指定端口
},
css: {
postcss:{
plugins:[
// postCssPxToRem({
// rootValue:37.5,
// propList:['*'],
// })
postCssPxToRem({
rootValue: 37.5,
propList: ['*'],
})
]
},
preprocessorOptions: {
scss: {}
scss: {
api: 'modern-compiler', // or 'modern'
}
}
},
plugins: [
vue(),
vueJsx(),
AutoImport({
resolvers: [ElementPlusResolver()],
resolvers: [VantResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
resolvers: [VantResolver()],
}),
],
resolve: {