Commit d2c4018b authored by kang.nie@inzymeits.com's avatar kang.nie@inzymeits.com
Browse files

初始化代码

parent de8a8ae5
Pipeline #3105 failed with stages
in 0 seconds
<template>
<div>
<div class="toggle-menu" @click="toggleMenu">
<img :class="isCollapse? 'toggle-menu-open' : ''" :src="require('@/assets/img/icon_menu.svg')" alt="" class="toggle-menu-icon">
</div>
<el-menu :default-active="activeIndex" :collapse="isCollapse" class="el-menu-vertical-demo" @select="onSelect" @open="onOpen" @close="onClose">
<el-submenu v-for="menu in powerMenus" :key="menu.name" :index="menu.path">
<template slot="title">
<i :class="menu.meta ? menu.meta.icon : ''" class="icon-menu" />
<span>{{ menu.meta ? menu.meta.title : '' }}</span>
</template>
<el-menu-item v-for="childMenu in menu.children" :key="childMenu.name" :index="childMenu.path">{{ childMenu.meta ? childMenu.meta.title : '' }}</el-menu-item>
</el-submenu>
</el-menu>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data() {
return {
isCollapse: false
}
},
computed: {
...mapGetters(['addRoutes']),
activeIndex() {
return this.$route.path
},
powerMenus() {
// 当前已经过滤后的数据
const menus = this.addRoutes.filter(route => ['Search', 'Management', 'Audit'].includes(route.name))
// 取出有孩子节点的
return menus.filter(menu => menu.children.length > 0)
}
},
methods: {
toggleMenu() {
this.isCollapse = !this.isCollapse
},
onSelect(path) {
this.$router.push({ path })
},
onOpen(key, keyPath) {
console.log(key, keyPath)
},
onClose(key, keyPath) {
console.log(key, keyPath)
}
}
}
</script>
<style lang="scss" scoped>
.toggle-menu {
position: absolute;
top: 299px;
right: 0;
width: 16px;
height: 60px;
padding-top: 23px;
padding-left: 3px;
background: url(../../assets/img/icon_menu_bg.svg) no-repeat 0 0;
background-size: 100% 100%;
z-index: 1;
cursor: pointer;
.toggle-menu-icon {
width: 14px;
height: 14px;
}
.toggle-menu-open{
transform: rotate(180deg);
}
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 208px;
}
</style>
<template>
<div>
<el-dialog
:visible.sync="dialogVisible"
:show-close="false"
width="482px"
center
custom-class="previewDialog"
>
<el-row class="operation-wait-dialog">
<el-col :span="14">
<el-row class="wait-info">
<el-col :span="3">
<img :src="require('@/assets/image/icon_wait.png')" alt="">
</el-col>
<el-col :span="15" class="wait-info-msg">
<p class="wait-info-msg-title">{{ promptInfo }}</p>
<p class="wait-info-msg-second">{{ time }}</p>
</el-col>
</el-row>
</el-col>
<el-col :span="10">
<img :src="require('@/assets/image/wait.png')" alt="操作等待">
</el-col>
</el-row>
<!-- <span slot="footer" class="dialog-footer">
<el-button @click="cancel">取 消</el-button>
<el-button type="primary" @click="cancel">确 定</el-button>
</span> -->
</el-dialog>
</div>
</template>
<script>
export default {
props: {
waitVisible: {
type: Boolean,
default: false
},
promptInfo: {
type: String,
default: () => ''
},
breakTimer: {
type: Boolean,
default: false
}
},
data() {
return {
time: 120,
intervals: null
}
},
computed: {
dialogVisible: {
get() {
return this.waitVisible
},
set(val) {
console.log(val)
this.$emit('updateVisible', val) // dialogVisible改变的时候通知父组件
}
}
},
watch: {
waitVisible(res) {
if (res) {
this.time = 120
this.reduce()
}
},
time(newVal) {
if (newVal === 0) {
this.$emit('updateWaitVisible', false)
}
},
breakTimer(val) {
if (val) {
clearTimeout(this.intervals)
this.time = 0
}
}
},
methods: {
cancel() {
this.$emit('updateWaitVisible', false)
},
/**
* 定时器方法
*/
reduce() {
this.intervals = setTimeout(() => {
if (this.time > 0) {
this.time--
this.reduce()
} else if (this.time === 0) {
clearTimeout(this.intervals)
this.time = 120
}
}, 1000)
}
}
}
</script>
<style scoped lang="scss">
.operation-wait-dialog{
.wait-info{
img{
width: 28px;
height: 28px;
margin-top: 16px;
}
.wait-info-msg{
text-align: left;
.wait-info-msg-title{
width: 396px;
height: 24px;
font-family: SourceHanSansCN-Bold;
font-size: 16px;
color: #111111;
line-height: 24px;
font-weight: 700;
}
.wait-info-msg-second{
width: 45px;
height: 20px;
opacity: 0.6;
font-family: SourceHanSansCN-Normal;
font-size: 14px;
color: #111111;
line-height: 24px;
font-weight: 400;
}
}
}
}
</style>
<style lang="scss">
.previewDialog{
.el-dialog__header {
display: none;
}
.el-dialog-content {
padding: 0;
overflow: unset;
}
}
</style>
<template>
<el-select :value="value" :placeholder="placeholder" clearable @change="handleCarCompanyChange">
<el-option v-for="item in list" v-bind="item" :key="item.value" />
</el-select>
</template>
<script>
import { queryCurrentSubOrgList, queryProtocalOrgByList } from '@/api/management'
export default {
name: 'CarCompany',
props: {
value: {
type: [String, Number],
default: ''
},
placeholder: {
type: String,
default: '请选择'
},
// 默认选中第一项
default: {
type: [String, Function],
default: ''
},
// 类型,1:车企 0:所有
type: {
type: Number,
default: 1
}
},
data() {
return {
list: []
}
},
mounted() {
this.queryProtocalOrgByList()
},
methods: {
/**
* 查询车厂列表
*/
async queryProtocalOrgByList() {
const data = await (this.type === 1 ? queryProtocalOrgByList : queryCurrentSubOrgList)()
// 如果查询出来了车企
if (data && data.length > 0) {
this.list = data.map(item => {
return {
value: item.uuid,
label: item.organName
}
})
}
// 如果当前需要默认选中
if (this.default) {
this.handleCarCompanyChange(typeof this.default === 'function' ? this.default(this.list) : this.default)
}
},
/**
* 车厂切换方法
* @param v 当前方法
*/
handleCarCompanyChange(v) {
this.$emit('input', v)
this.$emit('change', v)
}
}
}
</script>
<script>
import bridge, { isInApp } from '@/utils/bridge'
import CompSign from '@/components/CompSign'
import { queryPotocolManage } from '@/api/management'
const UPLOAD_TYPE = {
CAMERA: 'camera',
NATIVE: 'native',
SIGN: 'sign'
}
const UPLOAD_ICON = {
camera: 'camera',
native: 'document',
sign: 'edit'
}
export default {
name: 'PadBridge',
props: {
// 是否需要加密
security: {
type: Boolean,
default: false
},
// 上传类型,[camera, native, sign]
uploadType: {
type: Array,
default: () => ([UPLOAD_TYPE.CAMERA, UPLOAD_TYPE.NATIVE])
},
// 当上传类型有sign的时候,需要传入此字段标识当前需要取哪个文件来签名
signKey: {
type: String,
default: ''
},
// 附件url
attachmentUrl: {
type: String,
default: ''
}
},
data() {
return {
showPopOver: false,
isInApp,
showSign: false,
showSignLoading: false,
originUrl: '',
fileList: []
}
},
mounted() {
// 记录当前文件上传组件
this.elUpload = this.$slots.default[0].componentInstance
// 如果在app中,则禁用文件选择器,使用本地的拍照上传和文件选择
if (this.isInApp && this.elUpload.$el.querySelector('input[type=file]')) {
this.elUpload.$el.querySelector('input[type=file]').disabled = 'disabled'
}
},
methods: {
/**
* 拍照上传
*/
handleTakePhoto() {
// 如果当前是一体机
if (this.$store.getters.deviceType === 'aio') {
// 回调方法
const callback = (video, type) => {
// 如果当前拍摄的是视频
if (type === 'video') {
this.$message.error('请上传图片')
return
}
this.uploadFile(video)
}
// 获取一体机
bridge.getUvcCameraList((deviceList = []) => {
console.log('当前读取的设备列表:', deviceList)
// 是否有master相机
const masterCamera = deviceList.find(device => device.productName.toLowerCase().indexOf('master') >= 0)
// 如果当前有高拍仪的拍文件相机
if (masterCamera) {
bridge.openTargetUvcCamera(masterCamera, callback)
} else {
bridge.getUvcCamera(callback)
}
})
} else {
bridge.getCamera(this.uploadFile.bind(this))
}
// 关闭弹框
this.showPopOver = false
},
/**
* 选择文件上传
*/
handleSelectFile() {
bridge.getPhoto(this.uploadFile.bind(this))
},
/**
* 触发按钮的点击事件
* @param type 当前按钮的点击类型
*/
async handleClick(type) {
switch (type) {
case UPLOAD_TYPE.CAMERA:
this.handleTakePhoto()
break
case UPLOAD_TYPE.NATIVE:
this.handleSelectFile()
break
case UPLOAD_TYPE.SIGN:
// 签名的loading
this.showSignLoading = true
try {
// 请求接口
const agreements = await queryPotocolManage({ orgId: this.$store.getters.organId })
// 源url
const originUrlLink = agreements[this.signKey + 'Vertical']
// 源文件路径
this.originUrl = originUrlLink ? originUrlLink.fileUrl : ''
// 展示签名
this.showSign = true
} catch (error) {
console.error(error)
}
this.showSignLoading = false
break
}
},
/**
* 上传文件
*/
async uploadFile(file) {
// 关闭签名按钮
this.showSign = false
try {
this.elUpload.$children[0].uploadFiles([file])
} catch (error) {
console.error(error)
}
},
/**
* 上传文案的方式
* @param type 当前类型
*/
uploadText(type) {
return {
camera: '拍照上传',
native: '选择文件',
sign: '签字上传'
}[type]
}
},
render() {
// 如果当前是app内部
if (this.isInApp) {
const defaultSlot = this.$scopedSlots.default()
return (
<div>
<el-popover
value={this.showPopOver}
class='component-pad-bridge'
placement='bottom'
width='260'
trigger='click'
scopedSlots={{
reference() {
return defaultSlot
}
}}
onInput={state => {
this.showPopOver = state
}}
>
<div class='component-pad-bridge-operation-wrapper'>
{
this.uploadType.map(t => {
return <el-button loading={t === UPLOAD_TYPE.SIGN && this.showSignLoading} icon={`el-icon-${UPLOAD_ICON[t]}`} onClick={() => this.handleClick(t)}>{this.uploadText(t)}</el-button>
})
}
</div>
</el-popover>
<CompSign
title={this.title}
show={this.showSign}
originUrl={this.originUrl}
attachmentUrl={this.attachmentUrl}
onClose={() => {
this.showSign = false
}}
onChange={this.uploadFile}
/>
</div>
)
}
return this.$scopedSlots.default()
}
}
</script>
<style lang="scss">
.component-pad-bridge-operation-wrapper {
position: relative;
overflow: hidden;
}
.component-pad-bridge-vin-image {
background-color: #ffffff;
width: 750px;
height: 500px;
padding: 20px 40px;
position: fixed;
top: 0px;
left: 100vw;
h1 {
text-align: center;
}
.vin-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 16px;
}
.vin-content {
font-size: 14px;
}
}
</style>
<template>
<div class="page-module">
<div class="module-header">
<i class="module-icon"/>
<img :src="imgPath" class="module-icon">
<span class="module-name">{{ name }}</span>
</div>
<div class="module-body">
<slot/>
</div>
</div>
</template>
<script>
export default {
name: 'PageModule',
props: {
name: {
type: String,
default() {
return ''
}
},
icon: {
type: String,
default() {
return ''
}
},
path: {
type: String,
default: ''
}
},
computed: {
imgPath() {
return this.path ? this.path : `${process.env.VUE_APP_ROUTER}static/svg/${this.icon}.svg`
}
}
}
</script>
<style lang="scss">
.page-module {
margin: 0 auto 12px;
width: 960px;
background: #fff;
border-radius: 8px;
box-sizing: border-box;
padding: 0 32px 24px;
.module-header {
margin-bottom: 10px;
height: 62px;
display: flex;
align-items: center;
border-bottom: 1px dashed #EBEEF2;
}
.module-name {
margin-left: 8px;
font-size: 16px;
color: #212026;
font-weight: 700;
}
}
</style>
<template>
<div class="page-module-tabs">
<el-tabs :value="value" type="card" @tab-click="handleTabClick">
<el-tab-pane v-for="tab in tabs" :key="tab.value" :name="tab.value">
<div slot="label" class="page-module-header-tab"><img :class="tab.icon" :src="tab.icon" class="page-module-header-tab-icon"> {{ tab.label }}</div>
</el-tab-pane>
</el-tabs>
<slot />
</div>
</template>
<script>
export default {
name: 'PageModuleTabs',
props: {
tabs: {
type: Array,
default: () => []
},
value: {
type: String,
default: ''
}
},
methods: {
/**
* tab切换方法
*/
handleTabClick(tab) {
this.$emit('input', tab.name)
}
}
}
</script>
<style lang="scss">
.page-module-tabs {
margin: 0 auto 12px;
width: 960px;
background: #fff;
border-radius: 8px;
box-sizing: border-box;
padding: 12px 32px 24px;
.el-tabs__header {
border-color: #EBEEF2;
.el-tabs__nav {
border: none;
.el-tabs__item {
background: #F9FAFB;
border-radius: 4px 4px 0px 0px;
border: 1px solid #EBEEF2;
color: #212026;
font-size: 16px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
width: 152px;
line-height: 48px;
margin-right: 12px;
height: 48px;
.page-module-header-tab {
align-items: center;
display: flex;
opacity: 0.5;
justify-content: center;
.page-module-header-tab-icon {
display: inline-block;
width: 20px;
height: 20px;
margin-right: 8px;
}
}
&.is-active {
background-color: #ffffff;
border-bottom: 0;
color: #212026;
.page-module-header-tab {
opacity: 1;
}
}
}
}
}
}
</style>
<template>
<div class="page-title">
<div class="title-name">{{ pageTitle }}</div>
</div>
</template>
<script>
export default {
name: 'PageTitle',
props: {
title: {
type: String,
default() {
return ''
}
},
desc: {
type: String,
default() {
return ''
}
}
},
computed: {
pageTitle() {
return this.title || this.$route.meta.title
}
}
}
</script>
<style lang="scss">
.page-title {
margin-bottom: -28px;
height: 148px;
background-image: url(../../assets/image/title_bg.png);
background-repeat: no-repeat;
background-position: center;
background-size: auto 100%;
display: flex;
justify-content: center;
align-content: center;
.title-name {
margin: -8px auto 0;
display: inline-block;
align-self: center;
padding: 0 20px;
font-size: 30px;
color: #fff;
letter-spacing: 0;
text-align: center;
font-weight: 700;
position: relative;
&:before,
&:after {
content: '';
display: block;
width: 60px;
height: 2px;
position: absolute;
top: 50%;
margin-top: -1px;
}
&:before {
background-image: linear-gradient(90deg,hsla(0,0%,100%,.06) 12%,#fff 45%);
left: -60px;
}
&:after {
background-image: linear-gradient(270deg,hsla(0,0%,100%,.06) 12%,#fff 45%);
right: -60px;
}
}
}
</style>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>ico_清除条件@2x</title>
<g id="车企实名制系统" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="基础管理-账户管理" transform="translate(-366.000000, -260.000000)" fill-rule="nonzero">
<g id="编组-8" transform="translate(264.000000, 252.000000)">
<g id="编组-13" transform="translate(86.000000, 0.000000)">
<g id="编组-21" transform="translate(16.000000, 6.000000)">
<g id="清除" transform="translate(0.000000, 2.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M14.0484375,13.5875 L13.2203125,8.8125 L13.5,8.8125 C13.725,8.8125 13.90625,8.63125 13.90625,8.40625 L13.90625,5.40625 C13.90625,5.18125 13.725,5 13.5,5 L9.65625,5 L9.65625,2.15625 C9.65625,1.93125 9.475,1.75 9.25,1.75 L6.75,1.75 C6.525,1.75 6.34375,1.93125 6.34375,2.15625 L6.34375,5 L2.5,5 C2.275,5 2.09375,5.18125 2.09375,5.40625 L2.09375,8.40625 C2.09375,8.63125 2.275,8.8125 2.5,8.8125 L2.7796875,8.8125 L1.9515625,13.5875 C1.946875,13.6109375 1.9453125,13.634375 1.9453125,13.65625 C1.9453125,13.88125 2.1265625,14.0625 2.3515625,14.0625 L13.6484375,14.0625 C13.671875,14.0625 13.6953125,14.0609375 13.7171875,14.05625 C13.9390625,14.01875 14.0875,13.8078125 14.0484375,13.5875 Z M3.1875,6.09375 L7.4375,6.09375 L7.4375,2.84375 L8.5625,2.84375 L8.5625,6.09375 L12.8125,6.09375 L12.8125,7.71875 L3.1875,7.71875 L3.1875,6.09375 Z M10.5,12.96875 L10.5,10.53125 C10.5,10.4625 10.44375,10.40625 10.375,10.40625 L9.625,10.40625 C9.55625,10.40625 9.5,10.4625 9.5,10.53125 L9.5,12.96875 L6.5,12.96875 L6.5,10.53125 C6.5,10.4625 6.44375,10.40625 6.375,10.40625 L5.625,10.40625 C5.55625,10.40625 5.5,10.4625 5.5,10.53125 L5.5,12.96875 L3.16875,12.96875 L3.8734375,8.90625 L12.125,8.90625 L12.8296875,12.96875 L10.5,12.96875 Z" id="形状" fill="#999999"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
import { IS_ARRAY_STRING_REG, deconstruct } from './shared'
/**
* 钩子方法,可触发对应钩子
*/
function Hooks() {
this.hooks = []
}
/**
* 绑定钩子
* @param {String} type 钩子名称
* @param {Function} action 执行钩子触发的方法
*/
Hooks.prototype.bind = function(type, action) {
this.hooks[type] = action
}
/**
* 触发之前绑定的钩子
* @param {String} type 钩子类型
*/
Hooks.prototype.tap = function(type, params) {
this.hooks[type] && this.hooks[type](params)
}
// hook实例
const HooksIntance = new Hooks()
export const BEFORE_REQUEST = 'beforeRequest'
/**
* 绑定接口请求前触发的事件
* @param formData 当前表单触发的数据
*/
HooksIntance.bind(BEFORE_REQUEST, formData => {
// 校验当前接口请求数据中是否有需要解构的内容
Object.keys(formData).forEach(key => {
// 将key去掉空格
const noSpaceKey = key.replace(' ', '')
// 如果当前是数组字符串
if (IS_ARRAY_STRING_REG.test(noSpaceKey)) {
// 当前值
const formItemValue = formData[key] ? (
formData[key] instanceof Array ? formData[key] : `${formData[key] || ''}`.split(',')
) : []
// 将解构的数据放入数组中
deconstruct(noSpaceKey).forEach((mapKey, mapIndex) => {
formData[mapKey] = formItemValue[mapIndex]
})
// 删除原来的数据
delete formData[key]
}
})
})
export default HooksIntance
.component-search-table {
font-family: PingFangSC-Regular;
padding: 24px;
.component-search-table-container {
.component-search-table-nav-title {
color: #333841;
font-size: 20px;
font-weight: 500;
font-family: PingFangSC-Medium, PingFang SC;
line-height: 28px;
}
.component-search-table-form {
background: #FFFFFF;
border-radius: 8px;
padding: 24px;
margin-top: 16px;
.el-form-item {
margin-bottom: 0px;
.el-input__inner {
height: 32px;
line-height: 32px;
}
.el-date-editor {
display: block;
width: 100%;
height: 34px;
}
}
.component-search-table-btn-wrap {
display: flex;
.component-search-table-button {
border-radius: 4px;
display: block;
height: 32px;
padding: 0px 16px;
&.clear {
border: 1px solid #DCDFE6;
color: #999999;
margin-left: 8px;
}
.component-search-table-icon-clear {
background: url(./assets/ico_clear.svg) 100%;
display: inline-block;
width: 16px;
height: 16px;
margin-bottom: -2px;
}
}
}
}
.component-search-table-table {
background: #FFFFFF;
border-radius: 8px;
margin-top: 12px;
padding: 20px 24px 24px;
.component-search-table-action-bar {
align-items: center;
display: flex;
justify-content: space-between;
.title {
font-size: 16px;
font-weight: 500;
font-family: PingFangSC-Medium;
color: #212026;
}
.action-bar {
.el-button {
border-radius: 4px;
height: 32px;
padding: 0px 16px;
}
}
}
.el-table {
margin-top: 12px;
th.el-table__cell {
background: #F7F8FA;
color: #494B53;
font-weight: 500;
font-family: PingFangSC-Medium;
}
.el-button--text {
color: #2A68FF;
}
.cell {
padding: 0 10px;
}
}
.el-pagination {
text-align: right;
margin-top: 16px;
.btn-prev, .btn-next, .el-pagination__total, .el-input__inner {
color: #1D2129;
font-size: 14px;
height: 32px;
line-height: 32px;
}
.el-pagination__total {
margin-right: 20px;
}
.el-input__inner {
background: #F2F3F5;
border: 0px;
}
.el-pager {
.number {
border-radius: 2px;
color: #4E5969;
font-size: 14px;
width: 32px;
height: 32px;
line-height: 32px;
min-width: 32px;
margin-left: 8px;
&.active {
background-color: rgba(42, 104, 255, 0.1);
color: #2A68FF;
}
&:hover {
background-color: #F7F8FA;
}
}
}
}
}
}
}
\ No newline at end of file
<template>
<div class="component-search-table">
<div class="component-search-table-container">
<div class="component-search-table-nav-title">{{ title }}</div>
<div class="component-search-table-form">
<el-form ref="formRef" :model="value" label-position="left" size="small">
<el-row :gutter="12">
<slot name="slot-form" />
<el-col :span="6">
<div class="component-search-table-btn-wrap">
<el-button :loading="loading" type="primary" icon="el-icon-search" class="component-search-table-button" @click="handleSubmit()">查询</el-button>
<el-button class="component-search-table-button clear" @click="handleReset"><i class="component-search-table-icon-clear" />清除</el-button>
</div>
</el-col>
</el-row>
</el-form>
</div>
<div class="component-search-table-table">
<div class="component-search-table-action-bar">
<div class="title">{{ title }}</div>
<div class="action-bar">
<slot name="slot-action-bar" />
</div>
</div>
<slot name="slot-replace-table">
<el-table ref="tableRender" :data="list">
<slot name="slot-table" />
</el-table>
<el-pagination
v-show="total > 0"
:current-page="currPage"
:page-sizes="[10, 20, 50]"
:page-size="pageSize"
:total="total"
layout="total, prev, pager, next, sizes"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</slot>
</div>
</div>
</div>
</template>
<script>
import './index.scss'
export default {
props: {
title: {
type: String,
default: ''
},
value: {
type: Object,
default: () => ({})
},
// 是否立马请求接口
immediately: {
type: Boolean,
default: true
}
},
data() {
return {
loading: false,
list: [],
total: 0,
currPage: 1,
pageSize: 20
}
},
mounted() {
this.immediately && this.handleSubmit()
},
methods: {
/**
* 提交方法
*/
async handleSubmit(currPage = 1) {
try {
await this.$refs.formRef.validate()
} catch (error) {
console.error('校验失败:', error)
return
}
// 展示loading框
this.loading = true
this.currPage = currPage
// 取出总书记,每页条数以及每页展示总数
const { value, pageSize } = this
// 3s后如果没请求到接口也取消回调
const timer = setTimeout(() => {
this.loading = false
}, 3000)
// 提交方法
this.$emit(
'submit',
{ ...value, currPage, pageSize },
({ list, totalCount }) => {
clearTimeout(timer)
this.loading = false
this.list = list
this.total = totalCount
}
)
},
/**
* 每页展示条数改变方法
* @param pageSize 当前每页展示条数
*/
handleSizeChange(pageSize) {
// 记录当前每页展示条数
this.pageSize = pageSize
// 开始提交
this.handleSubmit()
},
/**
* 页码变化方法
* @param currPage 当前页码
*/
handleCurrentChange(currPage) {
this.handleSubmit(currPage)
},
/**
* 点击重置方法
*/
handleReset() {
// 将数据浅拷贝
const formData = { ...(this.value || {}) }
// 将所有值清空
Object.keys(formData).forEach(key => {
// 如果当前是数组
if (formData[key] instanceof Array) {
formData[key] = []
} else {
formData[key] = ''
}
})
// 开始双向绑定
this.$emit('input', formData)
// 重置数据
this.$emit('reset')
}
}
}
</script>
<template>
<div v-loading.fullscreen.lock="fullscreenLoading" element-loading-spinner="page-loading" class="app-container component-search-table">
<div class="component-search-table-container">
<FormRender
ref="formRenderRef"
v-model="formData"
:schema="formSchema"
:config="{ labelWidth: '75px', size: 'small', gutter: 20 }"
class="component-search-table-form"
@submit="formData => handleSubmit(formData)"
/>
<template slot="header">
<slot name="slot-operation-extend" />
</template>
<TableRender
:schema="tableSchema"
:data="list"
:pagination="pagination"
class="component-search-table-table"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template v-for="name in slotRender" v-slot:[name]="scope">
<slot :name="name" v-bind="scope" />
</template>
</TableRender>
</div>
</div>
</template>
<script>
import FormRender from '@/components/FormRender/col'
import TableRender from '@/components/TableRender'
import Hooks, { BEFORE_REQUEST } from './hooks'
import './index.scss'
export default {
name: 'SearchTable',
components: { FormRender, TableRender },
props: {
// 表单渲染schema
formSchema: {
type: Object,
default: () => ({})
},
// 表单配置
formConfig: {
type: Object,
default: () => ({})
},
// 表格渲染schema
tableSchema: {
type: Array,
default: () => ({})
},
// 分页配置
paginationConfig: {
type: Object,
default: () => ({
pageSizes: [10, 20, 50],
layout: 'total, prev, pager, next, sizes'
})
},
// 是否立即执行
immediately: {
type: Boolean,
default: true
},
// 表单的值
value: {
type: Object,
default: () => ({})
},
listName: {
type: String,
default: ''
}
},
data() {
return {
fullscreenLoading: false,
list: [],
formData: {
...this.value
},
pagination: {
total: 0,
currentPage: 1,
pageSize: 10
}
}
},
computed: {
title() {
if (this.listName) {
return this.listName
}
return this.$route.meta && this.$route.meta.title || ''
},
/**
* 需要渲染的slot
*/
slotRender() {
return this.tableSchema ? this.tableSchema.filter(schema => schema.slotRender).map(schema => schema.slotRender) : []
}
},
mounted() {
// 如果配置了立即执行,则页面渲染后立即执行submit方法
this.$nextTick(async() => {
// 如果是需要立即请求接口
if (this.immediately) {
await this.handleSubmit(this.formData)
}
})
},
methods: {
/**
* 校验方法
*/
refresh() {
// 开始校验
this.handleSubmit(this.formData, this.pagination.currentPage)
},
/**
* 点击了提交方法
*/
handleSubmit(formData = {}, currentPage = 1) {
// 接口请求的入参
const requestParams = {
// 表单输入的数据
...formData,
// 当前页码
currentPage,
// 每页数量
pageSize: this.pagination.pageSize
}
// 接口请求前处理数据
Hooks.tap(BEFORE_REQUEST, requestParams)
// 全局锁定
this.fullscreenLoading = true
// 记录当前页码
this.pagination.currentPage = currentPage
// 开始提交
this.$emit('submit', requestParams, (list, total) => {
// 记录当前页码
this.list = list
// 记录当前总页码
this.pagination.total = total
// 取消页面锁定状态
this.fullscreenLoading = false
})
},
/**
* 监听大小变化
* @param pageSize 页码大小
*/
handleSizeChange(pageSize) {
// 记录当前页码大小
this.pagination.pageSize = pageSize
// 开始校验输入数据
this.handleSubmit(this.formData)
},
/**
* 当前页码发生变化
*/
handleCurrentChange(currentPage) {
// 校验表单输入的数据
this.handleSubmit(this.formData, currentPage)
}
}
}
</script>
// 是否是正则数据
export const IS_ARRAY_STRING_REG = /\[[\S+,?]+\]/
/**
* 将数组形式的字符串解构成真实数组,并不校验不需要的单引号,双引号等
* @param target 等待解构的字符串
*/
export const deconstruct = target => {
// 先去除空格,单引号,以及双引号
const targetWithoutSpace = target.replace(/\s/g, '').replace(/\'/g, '').replace(/\"/, '')
// 如果当前是[target]这种形式,才参与解构
if (IS_ARRAY_STRING_REG.test(targetWithoutSpace)) {
return targetWithoutSpace.match(/([\w|\-|\$|\&])+/g)
}
throw new Error('解构格式不正确,请检查')
}
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>ico_双箭头_未点亮@2x</title>
<g id="车企实名制系统" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="企业实名-责任人信息" transform="translate(-1021.000000, -208.000000)" fill="#EDEEF0">
<g id="编组-9" transform="translate(240.000000, 180.000000)">
<g id="编组-7" transform="translate(781.000000, 24.000000)">
<g id="编组-6" transform="translate(0.000000, 4.000000)">
<path d="M2.7464194,2.87364782 L5.973,6.068 L6.02782537,6.11447931 L6.45207911,6.53873305 C6.55548349,6.64315249 6.61241949,6.77274527 6.62509311,6.90571855 C6.64247636,7.07758454 6.58575011,7.25647191 6.45416723,7.38935969 L6.45207911,7.39145814 L6.02782537,7.81571188 L5.973,7.861 L2.7464194,11.0565434 C2.53784454,11.2630701 2.21577734,11.2852802 1.98278909,11.1236733 L1.89998972,11.0544552 L1.47364785,10.628103 C1.2663984,10.4187983 1.24485169,10.0953788 1.40799874,9.86233915 L1.47783443,9.77958524 L4.32,6.964 L1.47783443,4.15060594 C1.24236661,3.91745031 1.24049222,3.53755595 1.47364785,3.30208813 C1.47434217,3.30138693 1.47503821,3.30068745 1.47573598,3.29998968 L1.89998972,2.87573594 C2.13348723,2.64223843 2.51177268,2.64130521 2.7464194,2.87364782 Z M8.7464194,2.87364782 L11.973,6.068 L12.0278254,6.11447931 L12.4520791,6.53873305 C12.5554835,6.64315249 12.6124195,6.77274527 12.6250931,6.90571855 C12.6424764,7.07758454 12.5857501,7.25647191 12.4541672,7.38935969 L12.4520791,7.39145814 L12.0278254,7.81571188 L11.973,7.861 L8.7464194,11.0565434 C8.53784454,11.2630701 8.21577734,11.2852802 7.98278909,11.1236733 L7.89998972,11.0544552 L7.47364785,10.628103 C7.2663984,10.4187983 7.24485169,10.0953788 7.40799874,9.86233915 L7.47783443,9.77958524 L10.32,6.964 L7.47783443,4.15060594 C7.24236661,3.91745031 7.24049222,3.53755595 7.47364785,3.30208813 C7.47434217,3.30138693 7.47503821,3.30068745 7.47573598,3.29998968 L7.89998972,2.87573594 C8.13348723,2.64223843 8.51177268,2.64130521 8.7464194,2.87364782 Z" id="形状结合"></path>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>ico_双箭头_未点亮@2x</title>
<g id="车企实名制系统" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="企业实名-责任人信息" transform="translate(-755.000000, -208.000000)" fill="#212026">
<g id="编组-9" transform="translate(240.000000, 180.000000)">
<g id="编组-7备份" transform="translate(515.000000, 24.000000)">
<g id="编组-6" transform="translate(0.000000, 4.000000)">
<path d="M2.7464194,2.87364782 L5.973,6.068 L6.02782537,6.11447931 L6.45207911,6.53873305 C6.55548349,6.64315249 6.61241949,6.77274527 6.62509311,6.90571855 C6.64247636,7.07758454 6.58575011,7.25647191 6.45416723,7.38935969 L6.45207911,7.39145814 L6.02782537,7.81571188 L5.973,7.861 L2.7464194,11.0565434 C2.53784454,11.2630701 2.21577734,11.2852802 1.98278909,11.1236733 L1.89998972,11.0544552 L1.47364785,10.628103 C1.2663984,10.4187983 1.24485169,10.0953788 1.40799874,9.86233915 L1.47783443,9.77958524 L4.32,6.964 L1.47783443,4.15060594 C1.24236661,3.91745031 1.24049222,3.53755595 1.47364785,3.30208813 C1.47434217,3.30138693 1.47503821,3.30068745 1.47573598,3.29998968 L1.89998972,2.87573594 C2.13348723,2.64223843 2.51177268,2.64130521 2.7464194,2.87364782 Z M8.7464194,2.87364782 L11.973,6.068 L12.0278254,6.11447931 L12.4520791,6.53873305 C12.5554835,6.64315249 12.6124195,6.77274527 12.6250931,6.90571855 C12.6424764,7.07758454 12.5857501,7.25647191 12.4541672,7.38935969 L12.4520791,7.39145814 L12.0278254,7.81571188 L11.973,7.861 L8.7464194,11.0565434 C8.53784454,11.2630701 8.21577734,11.2852802 7.98278909,11.1236733 L7.89998972,11.0544552 L7.47364785,10.628103 C7.2663984,10.4187983 7.24485169,10.0953788 7.40799874,9.86233915 L7.47783443,9.77958524 L10.32,6.964 L7.47783443,4.15060594 C7.24236661,3.91745031 7.24049222,3.53755595 7.47364785,3.30208813 C7.47434217,3.30138693 7.47503821,3.30068745 7.47573598,3.29998968 L7.89998972,2.87573594 C8.13348723,2.64223843 8.51177268,2.64130521 8.7464194,2.87364782 Z" id="形状结合"></path>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<template>
<div class="page-steps">
<div :style="{margin}" class="step-list">
<div v-for="(name, i) of data" :key="i" :class="{finished: index >= i}" class="step-item">0{{ i+1 }}. {{ name }}</div>
</div>
</div>
</template>
<script>
export default {
name: 'Steps',
props: {
data: {
type: Array,
default() {
return []
}
},
index: {
type: Number,
default() {
return 0
}
},
// 针对只有2个步骤两边需要填充更多白边时,如卡解绑UI稿
margin: {
type: String,
default() {
return '0 0 0 0'
}
}
}
}
</script>
<style lang="scss">
.page-steps {
margin: 0 auto 12px;
padding: 0 32px;
width: 960px;
height: 70px;
background: #fff;
border-radius: 8px;
box-sizing: border-box;
.step-list {
height: 70px;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
&:after {
content: '';
height: 4px;
background: radial-gradient(circle at 1px 1px,#edeef0 2px, transparent 0);
background-size: 10px 10px;
position: absolute;
top: 50%;
left: 0;
right: 0;
margin-top: -2px;
z-index: 1;
}
}
.step-item {
padding: 0 12px 0 34px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
background: #fff url(./icon_arrow.svg) no-repeat 12px center / 14px 14px;
color: rgba(132, 133, 138, 0.6);
position: relative;
z-index: 9;
&.finished {
color: #212026;
background: #fff url(./icon_arrow_finished.svg) no-repeat 12px center / 14px 14px;
}
&:first-child {
padding-left: 22px;
background-position: 0 center;
}
}
}
</style>
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
isExternal() {
return isExternal(this.iconClass)
},
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
display: inline-block;
}
</style>
export default {
functional: true,
props: {
row: Object,
render: Function
},
render(h, ctx) {
return ctx.props.render(h, ctx.props.row)
}
}
<template>
<div class="component-table-render">
<el-table ref="tableRender" v-bind="config.table" :class="className" :border="border" :data="data" :max-height="maxHeight">
<el-table-column v-for="(column, index) in schema" :key="index" show-overflow-tooltip v-bind="column">
<template slot-scope="scope">
<slot v-if="column.slotRender" :name="column.slotRender" v-bind="scope" />
<span v-else>{{ (scope.column.property === 'index' || scope.column.type === 'index') ? (scope.$index + 1) : (column.valueFormatter ? column.valueFormatter(scope.row) : scope.row[column.prop]) }}</span>
</template>
</el-table-column>
</el-table>
<el-pagination
v-show="pagination.total > 0"
v-bind="config.pagination"
:total="pagination.total"
:current-page="pagination.currentPage"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
export const DEFAULT_PAGINATION = {
pageSizes: [10, 20, 50],
layout: 'total, prev, pager, next, sizes'
}
export default {
name: 'TableRender',
props: {
config: {
type: Object,
default: () => ({
table: {},
pagination: DEFAULT_PAGINATION
})
},
schema: {
type: Array,
default: () => ([])
},
border: {
type: Boolean,
default: false
},
className: {
type: String,
default: ''
},
data: {
type: Array,
default: () => ([])
},
pagination: {
type: Object,
default: () => ({
total: 0,
currentPage: 1,
pageSize: 10
})
},
maxHeight: {
type: [String, Number],
default: 'auto'
}
},
methods: {
/**
* 当分页大小发生变化时
* @param pageSize 当前分页大小
*/
handleSizeChange(pageSize) {
this.$emit('size-change', pageSize)
},
/**
* 当页码发生变化时
* @param page 当前页码
*/
handleCurrentChange(page) {
this.$emit('current-change', page)
}
}
}
</script>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment