refactor: 重构项目结构并删除未使用的组件和代码

- 删除了多个未使用的组件,包括 Breadcrumb、Hamburger、mavonEditor 等
-移除了未使用的 vuex store模块和 getters
- 删除了未使用的 API 接口定义- 简化了 Navbar组件的结构
- 移除了全局的 resize 事件监听
This commit is contained in:
陈昱达
2025-07-04 19:13:39 +08:00
parent 310abb121d
commit d4162300cc
30 changed files with 30 additions and 1358 deletions

View File

@@ -51,6 +51,7 @@
"babel-eslint": "10.0.1",
"babel-jest": "23.6.0",
"chalk": "2.4.2",
"compression-webpack-plugin": "^8.0.1",
"connect": "3.6.6",
"eslint": "5.15.3",
"eslint-plugin-vue": "5.2.2",

View File

@@ -1,35 +0,0 @@
import request from '@/assets/js/utils/request'
import getUrl from '@/assets/js/utils/get-url'
// 登录
export function login(data) {
return request({
url: getUrl('/user/login', 0),
method: 'post',
data
})
}
// 获取个人信息
export function getInfo(token) {
return request({
url: getUrl('/user/info', 0),
method: 'get',
params: { token }
})
}
// 登出
export function logout() {
return request({
url: getUrl('/user/logout', 0),
method: 'post'
})
}
// 测试示例
export function indexUser() {
return request({
url: getUrl('/index/user', 0),
method: 'get'
})
}

View File

@@ -1,5 +1,4 @@
import router from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/assets/js/utils/auth' // get token from cookie

View File

@@ -1,78 +0,0 @@
<template>
<el-breadcrumb class="app-breadcrumb" separator-class="el-icon-arrow-right">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect fs16">{{ item.meta.title }}</span>
<span v-else>{{ item.meta.title }}</span>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import pathToRegexp from 'path-to-regexp'
export default {
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
created() {
this.getBreadcrumb()
},
methods: {
getBreadcrumb() {
// only show routes with meta.title
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
// const first = matched[0]
// if (!this.isDashboard(first)) {
// matched = [{ path: '/dashboard', meta: { title: 'Dashboard' } }].concat(matched)
// }
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},
isDashboard(route) {
const name = route && route.name
if (!name) {
return false
}
return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
},
pathCompile(path) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
}
}
}
</script>
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 8px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>

View File

@@ -1,39 +0,0 @@
<template>
<div style="padding: 0 15px;" @click="toggleClick">
<svg :class="{ 'is-active': isActive }" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64">
<path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
/>
</svg>
</div>
</template>
<script>
export default {
name: 'Hamburger',
props: {
isActive: {
type: Boolean,
default: false
}
},
methods: {
toggleClick() {
this.$emit('toggleClick')
}
}
}
</script>
<style scoped>
.hamburger {
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
}
.hamburger.is-active {
transform: rotate(180deg);
}
</style>

View File

@@ -1,79 +0,0 @@
<template>
<div class="mavonEditor-index-container">
<!-- 可视化富文本编辑器 书写代码-->
<mavonEditor :defaultOpen="defaultOpen" ref="mavonEditor" :toolbarsFla="false" v-model="content" :toolbars="toolbars" :ishljs="true"></mavonEditor>
</div>
</template>
<script>
import { mavonEditor } from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
export default {
name: 'mavonEditor-index',
components: { mavonEditor },
props: {
value: {
type: String
},
defaultOpen: {
type: String,
default: 'edit'
}
},
model: {
prop: 'value',
event: 'event1'
},
watch: {
value(o) {
this.content = o
},
content(o) {
this.$emit('event1', o)
}
},
data() {
return {
content: '',
toolbars: {
bold: true, // 粗体
italic: true, // 斜体
header: true, // 标题
underline: true, // 下划线
strikethrough: true, // 中划线
mark: true, // 标记
superscript: true, // 上角标
subscript: true, // 下角标
quote: true, // 引用
ol: true, // 有序列表
ul: true, // 无序列表
link: true, // 链接
imagelink: true, // 图片链接
code: true, // code
table: true, // 表格
fullscreen: true, // 全屏编辑
readmodel: true, // 沉浸式阅读
htmlcode: true, // 展示html源码
help: true, // 帮助
/* 1.3.5 */
undo: true, // 上一步
redo: true, // 下一步
trash: true, // 清空
save: true, // 保存触发events中的save事件
/* 1.4.2 */
navigation: true, // 导航目录
/* 2.1.8 */
alignleft: true, // 左对齐
aligncenter: true, // 居中
alignright: true, // 右对齐
/* 2.2.1 */
subfield: true, // 单双栏模式
preview: true // 预览
}
}
},
methods: {}
}
</script>
<style scoped></style>

View File

@@ -1,127 +0,0 @@
<template>
<div class="render-swiper-container" style="height: 480px">
<div class="swiper-container" ref="swiper" style="height: 500px">
<div class="swiper-wrapper" style="height: 480px">
<div v-for="item in 8" class="swiper-slide" style="height: 100%">
<div style="position: relative" class="swp">
<img src="../../assets/images/pages/img.png" alt="" style="width: 100px" class="mv10" />
<div class="text-left ph20 fs14" style="line-height: 25px">
<p>服务定位为90%选择居家养老的老人提供高品质生活服务</p>
<p class="mt10">项目介绍</p>
<p class="mb10">106项深度保洁全屋分10区细化清洁标准如厨房高温消毒卫生间防霉处理解决服务非标化痛点</p>
<div class="btn-btn">
<div style="position: absolute;z-index: 1;width: 100%;">
<div class="btn-btn-dashad"></div>
<img src="../../assets/images/pages/btn.png" alt="" />
</div>
<div style="position: absolute;z-index: 2">
<p class="mb10">医养管家套餐联合平安家庭医生提供用药提醒慢病监测及三甲医院绿色通道转诊服务</p>
<p class="mb10">适老改造+智能监护安装智能防跌检测设备降低居家风险30%</p>
<p class="mb20">创新点直营团队+260项SOP流程政府合作覆盖特困老人满意度达98.6%</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="swiper-pagination" v-if="showPagination"></div>
<div class="swiper-button-next" v-if="showNextBtb"></div>
<div class="swiper-button-prev" v-if="showNextBtb"></div>
</div>
</div>
</template>
<script>
import Swiper from 'swiper'
import 'swiper/css/swiper.min.css'
export default {
name: 'render-swiper',
data() {
return {
swiper: '',
defaultOptions: {
effect: 'coverflow',
slidesPerView: 3,
centeredSlides: true,
initialSlide: 0, //初始展示索引
direction: 'horizontal', //水平/垂直展示
speed: 300, //切换速度
grabCursor: false, //拖动时显示手势 鼠标覆盖Swiper 时指针会变成手掌形状,拖动时指针会变成抓手形状
watchSlidesProgress: true, // 子元素的活动进程
setWrapperSize: false, // Swiper使用flexbox布局(display: flex)开启这个设定会在Wrapper上添加等于slides相加的宽或高在对flexbox布局的支持不是很好的浏览器中可能需要用到。
virtualTranslate: false, // 滑动锁定
width: undefined, //宽度
height: undefined, //高度
breakpoints: '', //响应式设置
autoHeight: false, //自动高度。设置为 true 时wrapper 和container 会随着当前slide 的高度而发生变化。
loop: true,
autoplay: {
delay: 3000
},
// 切换效果
coverflowEffect: {
rotate: 0,
stretch: -10,
depth: 80,
modifier: 4,
slideShadows: false
}
// init: false
}
}
},
props: {
options: {
type: Object,
default: () => {
return {}
}
},
showPagination: {
type: Boolean,
default: false
},
showNextBtb: {
type: Boolean,
default: false
}
},
watch: {},
components: {},
filters: {},
methods: {},
created() {
let newOptions = Object.assign({}, this.defaultOptions, this.options)
this.$nextTick(() => {
newOptions.loopAdditionalSlides = newOptions.loopAdditionalSlides ? newOptions.loopAdditionalSlides : 3
this.swiper = new Swiper(this.$refs.swiper, newOptions)
})
},
mounted() {}
}
</script>
<style scoped lang="scss">
.swp {
background: #fff;
border-radius: 20px;
overflow: hidden;
height: 100%;
.btn-btn {
overflow: hidden;
width: 100%;
position: relative;
height: 500px;
img {
width: 100%;
height: 100%;
}
.btn-btn-dashad {
position: absolute;
top: 0;
left: -5%;
width: 110%;
height: 100%;
background: rgba(255, 255, 255, 0.8);
}
}
}
</style>

View File

@@ -1,24 +0,0 @@
<script>
export default {
name: 'index',
props: ['scope', 'item'],
render() {
let { scope, item } = this
return scope.row[item.prop] ? (
<div class="toolBox">
<el-tooltip content={scope.row[item.prop] && scope.row[item.prop] != '' ? scope.row[item.prop] : '2'} placement="right" effect="dark">
<div class="ellipsis">{scope.row[item.prop]}</div>
</el-tooltip>
</div>
) : (
<div class="ellipsis">{scope.row[item.prop]}</div>
)
}
}
</script>
<style scoped>
.toolBox {
display: inline-grid;
}
</style>

View File

@@ -1,37 +0,0 @@
<script>
// 自定义内容的组件 实现element 自带render函数
const RenderSlot = {
functional: true,
props: {
row: Object,
render: Function,
index: Number,
column: {
type: Object,
default: null
}
},
render: (h, data) => {
const params = {
row: data.props.row,
index: data.props.index
}
if (data.props.column) params.column = data.props.column
return data.props.render(h, params)
}
}
export default {
name: 'FormSlot',
props: ['item', 'scope'],
components: { RenderSlot },
render() {
let { item, scope } = this
return (
// <el-form-item prop={`"${item.ruleName}"`} rules={item.rules}>
<render-slot row={scope.row} render={item.render} index={scope.$index} column={item} />
// </el-form-item>
// </el-form>
)
}
}
</script>

View File

@@ -1,31 +0,0 @@
<script>
// 自定义内容的组件 实现element 自带render函数
const RenderSlot = {
functional: true,
props: {
row: Object,
render: Function,
index: Number,
column: {
type: Object,
default: null
}
},
render: (h, data) => {
const params = {
index: data.props.index
}
if (data.props.column) params.column = data.props.column
return data.props.render(h, params)
}
}
export default {
name: 'headerSlot',
props: ['item', 'scope'],
components: { RenderSlot },
render() {
let { item, scope } = this
return <render-slot render={item.headRender} index={scope.$index} column={item} />
}
}
</script>

View File

@@ -1,369 +0,0 @@
<template>
<div id="RenderTable-container">
<el-form :model="model" ref="model">
<el-table
:class="drag ? 'dragTable' : ''"
:data="model.data"
row-key="id"
lazy
@sort-change="sortChange"
ref="renderTable"
:border="border"
stripe
:header-align="align"
size="small"
default-expand-all
:max-height="maxHeight || null"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
:tree-props="{ children: 'childList' }"
>
<template v-for="(item, index) in columns">
<!--没有type 的模板-->
<el-table-column
:align="item.align"
:row-key="item.id"
:label="item.key"
:type="item.type"
:sortable="item.sortable"
:key="index"
:min-width="item.width ? item.width : 225"
:fixed="item.fixed"
v-if="!item.type"
>
<template #header="scope" v-if="item.headRender">
<header-slot :item="item" :scope="scope" />
</template>
<template #default="scope">
<!--表单校验-->
<el-form-item v-if="item.ruleCode" :prop="'data.' + scope.$index + '.' + item.ruleCode" :rules="item.ruleCode ? item.rules : []">
<body-slot :item="item" :scope="scope" v-if="item.render" />
</el-form-item>
<body-slot :item="item" :scope="scope" v-if="!item.ruleCode && item.render" />
<!--自定义render模板-->
<!--</el-form>-->
<!--自带的btn 不能有prop 属性-->
<div v-if="!item.selfBtn && !item.prop && !item.type && !item.render" :fixed="item.fixed ? item.fixed : fixed">
<el-button type="primary" size="mini" plain :disabled="disabled" @click="edit(scope.row, scope.$index)">编辑</el-button>
<el-button type="danger" size="mini" plain :disabled="disabled" @click="del(scope.row, scope.$index)">删除</el-button>
</div>
<!--如果渲染的是selfBtn-->
<div v-if="item.selfBtn && !item.render">
<el-button
v-for="(btn, btnIndex) in item.selfBtn"
:fixed="item.fixed ? item.fixed : fixed"
:disabled="item.disabled !== undefined ? item.disabled : disabled"
:type="btn.type ? btn.type : 'primary'"
:size="btn.size ? btn.size : 'mini'"
:key="btnIndex"
@click="handlerMethods(scope.row, scope.$index, btn)"
:label="btn.name"
>
{{ btn.name }}
</el-button>
</div>
<!--提示框 内部判断是否给单元格提示-->
<TooltipScope :scope="scope" :item="item" v-if="!item.type && !item.render && item.prop" />
</template>
</el-table-column>
<!--有设置type 的表格 type 设置的是selection index-->
<el-table-column
:key="index"
:row-key="item.id"
:align="align"
:label="item.key"
:type="item.type"
:width="item.width"
v-else-if="item.type === 'index' || item.type === 'selection'"
/>
<el-table-column
:key="index"
:row-key="item.id"
:align="align"
:label="item.key"
:type="item.type"
:width="item.width"
v-else-if="item.type === 'radio'"
>
<template #default="scope">
<el-radio v-model="check" :label="scope.$index" @change="setCheck($event, scope)">{{ null }}</el-radio>
</template>
</el-table-column>
</template>
<!--自定义默认按钮 ps:增加 一行 删除一行-->
<el-table-column v-if="deletion" :align="align" width="180" :fixed="fixed">
<template slot-scope="scope">
<div>
<el-button
type="primary"
plain
@click="
e => {
addRow(scope.row, scope.$index)
}
"
:disabled="disabled"
circle
icon="el-icon-plus"
size="medium"
class="tableBtn"
/>
<el-button
type="danger"
plain
@click="
e => {
delRow(scope.row, scope.$index)
}
"
:disabled="disabled"
circle
size="medium"
icon="el-icon-minus"
class="tableBtn"
/>
</div>
</template>
</el-table-column>
</el-table>
</el-form>
<!--表格分页-->
<div class="text-right" v-if="total">
<el-pagination
class="table-container"
@size-change="sizeChange"
@current-change="currentChange"
background
:currentPage="currentPage"
layout="prev,sizes, pager, next,jumper"
:page-sizes="[10, 20, 30, 40, 50]"
:total="total"
/>
</div>
</div>
</template>
<script>
import Sortable from 'sortablejs'
import TooltipScope from './component/TooltipScope'
import bodySlot from './component/bodySlot'
import HeaderSlot from './component/headerSlot'
/* * @Author: 陈昱达 * @Date: 2020-08-27 17:40:39 * @Last Modified by: 陈昱达 * @Last Modified time: 2020-08-27 17:40:39 */
export default {
name: 'RenderTable',
data() {
return {
fixed: 'right',
check: ''
}
},
computed: {
model(v) {
return {
data: v.data
}
}
},
components: { HeaderSlot, TooltipScope, bodySlot },
props: {
//是否拖拽
drag: {
type: Boolean,
default: false
},
//是否展示 删减行 按钮 和新增行按钮
deletion: {
type: Boolean,
default: true
},
//页码
currentPage: {
type: Number
},
//是否禁用表格自带按钮
disabled: {
type: Boolean,
default: false
},
//表格边框 border
border: {
type: Boolean,
default: true
},
// 是否剧中 align
align: {
type: String,
default: 'center'
},
// table data
data: {
type: Array
},
//columns
columns: {
type: Array
},
//total
total: {
type: Number
},
maxHeight: {
type: String
}
},
mounted() {
if (this.drag) {
this.rowDrop()
// this.columnDrop()
}
},
methods: {
// 单选选中
setCheck(e, scope) {
this.check = e
this.$emit('selectRow', { row: scope.row, index: scope.$index })
this.$emit('select-row', { row: scope.row, index: scope.$index })
},
sortChange(sortData) {
let filters = this.columns.filter(item => item.key === sortData.column.label)
if (filters && filters.length > 0) {
sortData.prop = filters[0].prop
}
this.$emit('sortChange', sortData)
this.$emit('sort-change', sortData)
},
//行拖拽
rowDrop() {
const tbody = document.querySelector('.dragTable .el-table__body-wrapper tbody')
Sortable.create(tbody, {
animation: 180,
onEnd: ({ newIndex, oldIndex }) => {
const currRow = this.data.splice(oldIndex, 1)[0]
this.data.splice(newIndex, 0, currRow)
}
})
},
//列拖拽 目前有问题 暂未解决
columnDrop() {
const wrapperTr = document.querySelector('.dragTable .el-table__header-wrapper tr')
Sortable.create(wrapperTr, {
animation: 180,
filter: '.el-table-column--selection',
delay: 0,
onEnd: evt => {
const oldItem = this.columns[evt.oldIndex]
this.columns.splice(evt.oldIndex, 1)
this.columns.splice(evt.newIndex, 0, oldItem)
}
})
},
//edit in table
edit(row, index) {
this.$emit('edit', row, index)
},
// del In Table
del(row, index) {
this.$emit('del', row, index)
},
//展示条数改变
sizeChange(size) {
this.$emit('sizeChange', size)
this.$emit('size-change', size)
},
//页码改变
currentChange(current) {
this.$emit('currentChange', current)
this.$emit('current-change', current)
},
//根据数据的格式去返回对应的数据结构
getDataType(type) {
let dataType = ''
if (type === '[object Array]') {
dataType = []
} else if (type === '[object Object]') {
dataType = {}
}
return dataType
},
//Add a new line
addRow(row, index) {
let newRow = JSON.parse(JSON.stringify(row))
for (let item in newRow) {
newRow[item] = this.getDataType(Object.prototype.toString.call(newRow[item]))
}
this.data.splice(index + 1, 0, newRow)
this.$emit('buttonAdd', row, newRow)
this.$emit('button-add', row, newRow)
},
//Delete row
delRow(row, index) {
this.$emit('buttonDel', row, index)
this.$emit('button-del', row, index)
let newRow = JSON.parse(JSON.stringify(row))
this.data.splice(index, 1)
if (this.data.length === 0) {
for (let item in newRow) {
newRow[item] = this.getDataType(Object.prototype.toString.call(newRow[item]))
}
this.data.push(newRow)
}
},
//btnSelf 把当前数据提交给父组件自定义method事件中
handlerMethods(row, index, btn) {
this.$emit(btn.method, { row, index })
},
handleRowClick(row, column, event) {
this.$emit('row-click', row, column, event)
},
handleSelectionChange(row) {
this.$emit('selection-change', row)
},
//具体查看element 文档
toggleRowSelection(rows) {
this.$nextTick(() => {
if (rows) {
rows.forEach(row => {
this.$refs.renderTable.toggleRowSelection(row)
})
} else {
this.$refs.multipleTable.clearSelection()
}
})
},
clearSelection() {
this.$nextTick(() => {
this.$refs.renderTable.clearSelection()
})
}
}
}
</script>
<style scoped lang="scss">
.pd5 {
padding-left: 5px;
}
.tableBtn {
padding: 5px;
}
/deep/.el-table {
.el-radio__label {
padding: unset;
}
.el-form-item__error {
position: unset;
}
.el-form-item__content {
line-height: unset;
}
.el-form-item__error {
position: unset;
}
.el-form-item {
margin: 0;
}
}
</style>

View File

@@ -1,62 +0,0 @@
<template>
<div id="vueEditor-container">
<!--富文本编辑器-->
<VueEditor v-model="content" :editorToolbar="editorToolbar" />
</div>
</template>
<script>
import { VueEditor } from 'vue2-editor'
export default {
name: 'vueEditor',
components: { VueEditor },
props: {
value: {
type: String
}
},
model: {
prop: 'value',
event: 'event1'
},
watch: {
value(o, v) {
this.content = o
},
content(o) {
this.$emit('event1', o)
}
},
data() {
return {
content: this.value,
editorToolbar: [
[{ header: [false, 1, 2, 3, 4, 5, 6] }],
['bold', 'italic', 'underline', 'strike'], // toggled buttons
[{ align: ['', 'center', 'right', 'justify'] }],
['blockquote'],
[{ list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
[{ indent: '-1' }, { indent: '+1' }], // outdent/indent
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
['clean','link', 'image']
]
}
},
methods: {
handleImageAdded(file, Editor, cursorLocation, resetUploader) {
let formData = new FormData()
formData.append('file', file) //第一个file 后台接收的参数名
uploadFileComponentForEditor(formData)
.then(result => {
let url = result.content.fileUrl // 返回给你的图片路径
Editor.insertEmbed(cursorLocation, 'image', url)
})
.catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped></style>

1
src/icons/svg/search.svg Normal file
View File

@@ -0,0 +1 @@
<svg t="1751627459592" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2770" width="20" height="20"><path d="M473.142857 54.514286c-231.2 0-418.742857 187.428571-418.742857 418.742857 0 231.2 187.428571 418.742857 418.742857 418.742857 102.742857 0 196.8-37.028571 269.6-98.285714L918.171429 969.142857l50.514285-50.514286-175.428571-175.657142c61.485714-72.914286 98.514286-167.085714 98.514286-269.828572 0.114286-231.2-187.428571-418.628571-418.628572-418.628571z m0 765.828571c-46.857143 0-92.342857-9.142857-135.085714-27.2-41.371429-17.485714-78.514286-42.514286-110.4-74.4-31.885714-31.885714-56.914286-69.028571-74.4-110.4-18.057143-42.742857-27.2-88.228571-27.2-135.085714s9.142857-92.342857 27.2-135.085714c17.485714-41.371429 42.514286-78.514286 74.4-110.4 31.885714-31.885714 69.028571-56.914286 110.4-74.4 42.742857-18.057143 88.228571-27.2 135.085714-27.2s92.342857 9.142857 135.085714 27.2c41.371429 17.485714 78.514286 42.514286 110.4 74.4 31.885714 31.885714 56.914286 69.028571 74.4 110.4 18.057143 42.742857 27.2 88.228571 27.2 135.085714s-9.142857 92.342857-27.2 135.085714c-17.485714 41.371429-42.514286 78.514286-74.4 110.4s-69.028571 56.914286-110.4 74.4c-42.742857 18.057143-88.228571 27.2-135.085714 27.2z" fill="#ffffff" p-id="2771"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,12 +1,11 @@
import Vue from 'vue'
import 'normalize.css/normalize.css' // A modern alternative to CSS
import App from './App'
import store from './store'
import router from './router'
import Filters from './filters'
// import Filters from './filters'
import generatedComponents from './generatedComponents'
// 注册过滤器
Object.keys(Filters).forEach(k => Vue.filter(k, Filters[k]))
// Object.keys(Filters).forEach(k => Vue.filter(k, Filters[k]))
for (let item in generatedComponents) {
Vue.component(item, generatedComponents[item])
}
@@ -16,6 +15,5 @@ import '@/assets/js/utils/permission' // permission control
new Vue({
el: '#app',
router,
store,
render: h => h(App)
}).$mount('#app')

View File

@@ -1,9 +0,0 @@
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
sidebarList: state => state.user.sidebarList,
}
export default getters

View File

@@ -1,17 +0,0 @@
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import app from './modules/app'
import settings from './modules/settings'
import user from './modules/user'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app,
settings,
user,
},
getters
})
export default store

View File

@@ -1,55 +0,0 @@
import Cookies from 'js-cookie'
const state = {
sidebar: {
opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
withoutAnimation: false
},
device: 'desktop',
sidebarList: []
}
const mutations = {
TOGGLE_SIDEBAR: state => {
state.sidebar.opened = !state.sidebar.opened
state.sidebar.withoutAnimation = false
if (state.sidebar.opened) {
Cookies.set('sidebarStatus', 1)
} else {
Cookies.set('sidebarStatus', 0)
}
},
CLOSE_SIDEBAR: (state, withoutAnimation) => {
Cookies.set('sidebarStatus', 0)
state.sidebar.opened = false
state.sidebar.withoutAnimation = withoutAnimation
},
TOGGLE_DEVICE: (state, device) => {
state.device = device
},
SET_SIDEBAR_LIST: (state, sidebar) => {
state.sidebarList = sidebar
}
}
const actions = {
toggleSideBar({ commit }) {
commit('TOGGLE_SIDEBAR')
},
closeSideBar({ commit }, { withoutAnimation }) {
commit('CLOSE_SIDEBAR', withoutAnimation)
},
toggleDevice({ commit }, device) {
commit('TOGGLE_DEVICE', device)
},
setSidebarList({ commit }, sidebarList) {
commit('SET_SIDEBAR_LIST', sidebarList)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@@ -1,31 +0,0 @@
import defaultSettings from '@/assets/js/utils/settings'
const { showSettings, fixedHeader, sidebarLogo } = defaultSettings
const state = {
showSettings: showSettings,
fixedHeader: fixedHeader,
sidebarLogo: sidebarLogo
}
const mutations = {
CHANGE_SETTING: (state, { key, value }) => {
console.log(state, {key, value},'23232');
if (state.hasOwnProperty(key)) {
state[key] = value
}
}
}
const actions = {
changeSetting({ commit }, data) {
commit('CHANGE_SETTING', data)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@@ -1,97 +0,0 @@
import { login, logout, getInfo } from '@/api/app/user'
import { getToken, setToken, removeToken } from '@/assets/js/utils/auth'
const state = {
token: getToken(),
name: '',
avatar: ''
}
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
}
}
const actions = {
getSid({ commit }, data) {
commit('SET_SID', data)
},
setGroupList({ commit }, data) {
commit('SET_GROUPLIST', data)
},
setToken({ commit },data){
commit('SET_TOKEN', data)
},
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password })
.then(res => {
commit('SET_TOKEN', res.token)
setToken(res.token)
resolve()
})
.catch(error => {
reject(error)
})
})
},
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token)
.then(res => {
if (!res) {
reject('Verification failed, please Login again.')
}
commit('SET_NAME', res.name)
commit('SET_AVATAR', res.avatar)
resolve(res)
})
.catch(error => {
reject(error)
})
})
},
// user logout
logout({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token)
.then(() => {
commit('SET_TOKEN', '')
removeToken()
resolve()
})
.catch(error => {
reject(error)
})
})
},
// remove token
resetToken({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
removeToken()
resolve()
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@@ -6,23 +6,17 @@
</div>
<div class="text-right">
<input type="text" class="nav-input" />
<el-icon class="el-icon-search ml10 white" size="20px"></el-icon>
<svg-icon icon-class="search" style="margin-left: 15px;cursor: pointer"></svg-icon>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
import url from '@/assets/images/active.png'
export default {
components: {
Breadcrumb,
Hamburger
},
components: {},
data() {
return {
keyIndex: 0,
@@ -31,18 +25,8 @@ export default {
ls: [{ name: '关于幸福' }, { name: '发展历程' }, { name: '荣誉资质' }, { name: '联系我们' }]
}
},
computed: {
...mapGetters(['sidebar', 'avatar'])
},
methods: {
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
},
async logout() {
await this.$store.dispatch('user/logout')
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
}
}
computed: {},
methods: {}
}
</script>

View File

@@ -1,3 +1,3 @@
export { default as Navbar } from './Navbar'
export { default as Sidebar } from './Sidebar'
// export { default as Sidebar } from './Sidebar'
export { default as AppMain } from './AppMain'

View File

@@ -1,5 +1,5 @@
<template>
<div :class="classObj" class="app-wrapper">
<div class="app-wrapper">
<div class="app-wrapper-box">
<!-- <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
<sidebar class="sidebar-container" />
@@ -14,14 +14,11 @@
</template>
<script>
import { Navbar, Sidebar, AppMain } from './components'
import ResizeMixin from './mixin/ResizeHandler'
import { AppMain } from './components'
export default {
name: 'Layout',
components: {
Navbar,
Sidebar,
AppMain
},
data() {
@@ -29,25 +26,25 @@ export default {
type: process.env.VUE_APP_FLAG
}
},
mixins: [ResizeMixin],
// mixins: [ResizeMixin],
computed: {
sidebar() {
return this.$store.state.app.sidebar
},
device() {
return this.$store.state.app.device
},
fixedHeader() {
return this.$store.state.settings.fixedHeader
},
classObj() {
return {
hideSidebar: !this.sidebar.opened,
openSidebar: this.sidebar.opened,
withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device === 'mobile'
}
}
// sidebar() {
// return this.$store.state.app.sidebar
// },
// device() {
// return this.$store.state.app.device
// },
// fixedHeader() {
// return this.$store.state.settings.fixedHeader
// },
// classObj() {
// return {
// hideSidebar: !this.sidebar.opened,
// openSidebar: this.sidebar.opened,
// withoutAnimation: this.sidebar.withoutAnimation,
// mobile: this.device === 'mobile'
// }
// }
},
methods: {
handleClickOutside() {

View File

@@ -1,4 +1,4 @@
import store from '@/store'
// import store from '@/store'
const { body } = document
const WIDTH = 992 // refer to Bootstrap's responsive design

View File

@@ -1,5 +0,0 @@
module.exports = {
env: {
jest: true
}
}

View File

@@ -1,98 +0,0 @@
import { mount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
import ElementUI from 'element-ui'
import Breadcrumb from '@/components/Breadcrumb/index.vue'
const localVue = createLocalVue()
localVue.use(VueRouter)
localVue.use(ElementUI)
const routes = [
{
path: '/',
name: 'home',
children: [{
path: 'dashboard',
name: 'dashboard'
}]
},
{
path: '/menu',
name: 'menu',
children: [{
path: 'menu1',
name: 'menu1',
meta: { title: 'menu1' },
children: [{
path: 'menu1-1',
name: 'menu1-1',
meta: { title: 'menu1-1' }
},
{
path: 'menu1-2',
name: 'menu1-2',
redirect: 'noredirect',
meta: { title: 'menu1-2' },
children: [{
path: 'menu1-2-1',
name: 'menu1-2-1',
meta: { title: 'menu1-2-1' }
},
{
path: 'menu1-2-2',
name: 'menu1-2-2'
}]
}]
}]
}]
const router = new VueRouter({
routes
})
describe('Breadcrumb.vue', () => {
const wrapper = mount(Breadcrumb, {
localVue,
router
})
it('dashboard', () => {
router.push('/dashboard')
const len = wrapper.findAll('.el-breadcrumb__inner').length
expect(len).toBe(1)
})
it('normal route', () => {
router.push('/menu/menu1')
const len = wrapper.findAll('.el-breadcrumb__inner').length
expect(len).toBe(2)
})
it('nested route', () => {
router.push('/menu/menu1/menu1-2/menu1-2-1')
const len = wrapper.findAll('.el-breadcrumb__inner').length
expect(len).toBe(4)
})
it('no meta.title', () => {
router.push('/menu/menu1/menu1-2/menu1-2-2')
const len = wrapper.findAll('.el-breadcrumb__inner').length
expect(len).toBe(3)
})
// it('click link', () => {
// router.push('/menu/menu1/menu1-2/menu1-2-2')
// const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner')
// const second = breadcrumbArray.at(1)
// console.log(breadcrumbArray)
// const href = second.find('a').attributes().href
// expect(href).toBe('#/menu/menu1')
// })
// it('noRedirect', () => {
// router.push('/menu/menu1/menu1-2/menu1-2-1')
// const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner')
// const redirectBreadcrumb = breadcrumbArray.at(2)
// expect(redirectBreadcrumb.contains('a')).toBe(false)
// })
it('last breadcrumb', () => {
router.push('/menu/menu1/menu1-2/menu1-2-1')
const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner')
const redirectBreadcrumb = breadcrumbArray.at(3)
expect(redirectBreadcrumb.contains('a')).toBe(false)
})
})

View File

@@ -1,18 +0,0 @@
import { shallowMount } from '@vue/test-utils'
import Hamburger from '@/components/Hamburger/index.vue'
describe('Hamburger.vue', () => {
it('toggle click', () => {
const wrapper = shallowMount(Hamburger)
const mockFn = jest.fn()
wrapper.vm.$on('toggleClick', mockFn)
wrapper.find('.hamburger').trigger('click')
expect(mockFn).toBeCalled()
})
it('prop isActive', () => {
const wrapper = shallowMount(Hamburger)
wrapper.setProps({ isActive: true })
expect(wrapper.contains('.is-active')).toBe(true)
wrapper.setProps({ isActive: false })
expect(wrapper.contains('.is-active')).toBe(false)
})
})

View File

@@ -1,22 +0,0 @@
import { shallowMount } from '@vue/test-utils'
import SvgIcon from '@/components/SvgIcon/index.vue'
describe('SvgIcon.vue', () => {
it('iconClass', () => {
const wrapper = shallowMount(SvgIcon, {
propsData: {
iconClass: 'test'
}
})
expect(wrapper.find('use').attributes().href).toBe('#icon-test')
})
it('className', () => {
const wrapper = shallowMount(SvgIcon, {
propsData: {
iconClass: 'test'
}
})
expect(wrapper.classes().length).toBe(1)
wrapper.setProps({ className: 'test' })
expect(wrapper.classes().includes('test')).toBe(true)
})
})

View File

@@ -1,30 +0,0 @@
import { formatTime } from '@/utils/index.js'
describe('Utils:formatTime', () => {
const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01"
const retrofit = 5 * 1000
it('ten digits timestamp', () => {
expect(formatTime((d / 1000).toFixed(0))).toBe('7月13日17时54分')
})
it('test now', () => {
expect(formatTime(+new Date() - 1)).toBe('刚刚')
})
it('less two minute', () => {
expect(formatTime(+new Date() - 60 * 2 * 1000 + retrofit)).toBe('2分钟前')
})
it('less two hour', () => {
expect(formatTime(+new Date() - 60 * 60 * 2 * 1000 + retrofit)).toBe('2小时前')
})
it('less one day', () => {
expect(formatTime(+new Date() - 60 * 60 * 24 * 1 * 1000)).toBe('1天前')
})
it('more than one day', () => {
expect(formatTime(d)).toBe('7月13日17时54分')
})
it('format', () => {
expect(formatTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54')
expect(formatTime(d, '{y}-{m}-{d}')).toBe('2018-07-13')
expect(formatTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54')
})
})

View File

@@ -1,28 +0,0 @@
import { parseTime } from '@/utils/index.js'
describe('Utils:parseTime', () => {
const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01"
it('timestamp', () => {
expect(parseTime(d)).toBe('2018-07-13 17:54:01')
})
it('ten digits timestamp', () => {
expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01')
})
it('new Date', () => {
expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01')
})
it('format', () => {
expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54')
expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13')
expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54')
})
it('get the day of the week', () => {
expect(parseTime(d, '{a}')).toBe('五') // 星期五
})
it('get the day of the week', () => {
expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日
})
it('empty argument', () => {
expect(parseTime()).toBeNull()
})
})

View File

@@ -1,17 +0,0 @@
import { validUsername, isExternal } from '@/utils/validate.js'
describe('Utils:validate', () => {
it('validUsername', () => {
expect(validUsername('admin')).toBe(true)
expect(validUsername('editor')).toBe(true)
expect(validUsername('xxxx')).toBe(false)
})
it('isExternal', () => {
expect(isExternal('https://github.com/PanJiaChen/vue-element-admin')).toBe(true)
expect(isExternal('http://github.com/PanJiaChen/vue-element-admin')).toBe(true)
expect(isExternal('github.com/PanJiaChen/vue-element-admin')).toBe(false)
expect(isExternal('/dashboard')).toBe(false)
expect(isExternal('./dashboard')).toBe(false)
expect(isExternal('dashboard')).toBe(false)
})
})