引入组件
<div class="element-style">
<!-- button透明按钮 -->
<el-button plain class="style-button">透明按钮</el-button>
<!-- Divider 分割线 -->
<el-divider class="style-divider"> 以上属性不可调整 </el-divider>
<!-- progress进度条 -->
<el-progress class="style-progress" color="#0FF8F8" :format="() => ''" :percentage="50" />
<!-- input输入框 -->
<el-input v-model="value" class="style-input" placeholder="请输入"></el-input>
<!-- textarea输入框 -->
<el-input v-model="textareaValue" class="style-textarea" :rows="2" type="textarea" placeholder="请输入" />
<!-- select下拉框 -->
<el-select v-model="selectValue" popper-class="popper-default" class="style-select" placeholder="请选择" size="default"
@change="selectChange">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<!-- dropdown下拉菜单 -->
<el-dropdown class="style-dropdown" popper-class="popper-default">
<span class="el-dropdown-link">
导入
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>导入站房</el-dropdown-item>
<el-dropdown-item>导入杆塔</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- radio单选框组 -->
<el-radio-group class="style-radios" v-model="adioValue">
<el-radio v-for="(item, i) in radios" :key="item" :label="i">{{ item }}</el-radio>
</el-radio-group>
<!-- switch开关 -->
<el-switch class="style-switch" v-model="switcValue" />
<!-- Input Number 数字输入框 -->
<el-input-number class="style-inputNumber" v-model="inputNumber" :min="1" :max="10" />
<!-- 单联date-picker日期选择器 -->
<el-date-picker v-model="dateValue" class="style-input style-picker" popper-class="popper-default" value-format="YYYY-MM-DD"
type="date" placeholder="请选择日期" size="default" />
<!-- 双联date-picker日期选择器 -->
<el-date-picker v-model="dateValue" class="style-input style-picker" popper-class="popper-default" value-format="MM-DD"
type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" size="default"
@change="dateChange" />
<!-- Popover 气泡卡片 -->
<el-popover popper-class="popper-default" placement="bottom" :width="140" trigger="hover">
<template #reference>
<div>Popover入口</div>
</template>
<div>卡片内容</div>
</el-popover>
<!-- drawer抽屉 -->
<el-drawer v-model="visible" class="style-drawer" direction="rtl">
<template #header>
<div>标题</div>
</template>
<template #default>
<div>内容</div>
</template>
<template #footer>
<el-button color="#0ff8f8" @click="confirmClick">确定</el-button>
<el-button plain @click="cancelClick">取消</el-button>
</template>
</el-drawer>
<!-- tag标签 -->
<!-- 事件传额外参数 -->
<el-check-tag class="style-tag" :checked="true" @change="(status) => onChange(status, '参数')">
<span>tag内容</span>
</el-check-tag>
<!-- card卡片 -->
<el-card class="style-card">故障定位</el-card>
<!-- dialog弹窗 -->
<el-dialog
v-model="orderStore.showNotifyDialog"
:title="title"
:width="width || 640"
align-center
class="dialog-default"
@close="handleClose"
>
<div>333333333333333333333333333333333</div>
<template #footer>
<el-button plain class="style-button" @click="handleClose">取消</el-button>
<el-button color="#0ff8f8" @click="handleSend">发送</el-button>
</template>
</el-dialog>
<!-- menu菜单 -->
<el-menu unique-opened default-active="1" class="style-menu" @open="handleOpen" @close="handleClose">
<el-menu-item index="1">
<span>网架概览</span>
</el-menu-item>
<el-sub-menu index="2">
<template #title>
<span>统计维度</span>
</template>
<el-menu-item index="2-1">线路长度(km)</el-menu-item>
<el-menu-item index="2-2">杆塔基数</el-menu-item>
<el-menu-item index="2-3">接头盒数量</el-menu-item>
<el-menu-item index="2-4">光纤利用率</el-menu-item>
</el-sub-menu>
</el-menu>
<!-- checkbox 多选框 -->
<el-checkbox class="style-checkbox" v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">全部</el-checkbox>
<el-checkbox-group class="style-checkbox" v-model="checkedList" @change="handleCheckedChange">
<el-checkbox v-for="option in checkOptions[activeTab]" :key="option" :label="option">
{{ option }}
</el-checkbox>
</el-checkbox-group>
</div>
修改内置样式
/* 字体颜色 */
$color-theme: #0ff8f8; // 青蓝色主题色
$color-theme-hover: #97ffff; // 鼠标悬浮主题色
$color-white: #ffffff; // 白色,正文标题颜色
$color-black: #333; // 黑色
$color-subtitle: #acaeb3; // 浅灰色,次强调
$color-purple: #303774; // 深紫色
$color-blue: #409eff; // 深蓝色
$color-black: #131725; // 黑色调
$color-red: #f45151; // 红色调
$color-warning: #ff7d00; // 警告色
/* 字体大小 */
$font-size-default: 14px; //默认字体大小
$font-size-12: 12px;
$font-size-16: 16px;
$font-size-18: 18px;
$font-size-20: 20px;
$font-size-24: 24px;
/* 字体粗细 */
$font-weight: 600;
/* 背景颜色 */
$bg-theme: #070b1a; // 深色主题背景
$bg-transparent: transparent; // 透明
$bg-white: #ffffff; // 白色
$bg-default: #000000; // 默认黑色
$bg-gray: #393c48; // 深灰色
$bg-purple: #352c4e; // 深紫色
$bg-li-hover: #0c4649; // 鼠标悬浮列表选背景色
$bg-switch-active: #1ec2c2; // 开关背景色
$bg-switch: #43476b; // 开关背景色
$bg-checkbox-active: #13a6ad; // 多选选中
/* 边框 */
$border-default: 1px solid #797979; // 默认边框大小及颜色
$border-theme: 1px solid #125b64; // 主题边框
$border-purple: 1px solid #323869; // 紫色
$border-theme-hover: 1px solid #0ff8f8; // 鼠标悬浮主题边框
$border-radio: 3px solid #0a868c;
.element-style {
/* button按钮 */
.style-button {
border-radius: 0;
/* 素按钮 */
&.is-plain {
background: $bg-transparent;
color: $color-white;
border: $border-default;
&:hover {
color: $color-theme;
border: $border-hover;
}
}
}
/* Divider 分割线 */
.style-divider {
border-top: $border-default;
/* 中间文本 */
.el-divider__text {
width: 120px;
text-align: center;
background: $bg-theme;
color: $color-subtitle;
}
}
/* 进度条 */
.style-progress {
width: 350px;
/* 背景 */
.el-progress-bar__outer {
background: $bg-purple;
border-radius: 0;
/* 进度条 */
.el-progress-bar__inner {
border-radius: 0;
}
}
/* 进度条描述文本 */
.el-progress__text {
min-width: 0;
}
}
/* dropdown下拉菜单面板 */
.style-dropdown {
padding: 8px 0 8px;
color: #000;
font-size: 12px;
}
/* input输入框 */
.style-input {
/* 容器 */
.el-input__wrapper {
box-shadow: unset !important;
background: unset;
border: $border-theme;
/* 选中,聚焦,悬浮 */
&.is-active,
&.is-focus,
&:hover {
box-shadow: unset !important;
border: $border-theme-hover;
}
/* 点击后 */
.el-input.is-focus .el-input__wrapper,
.el-input__wrapper.is-focus {
box-shadow: unset !important;
border: $border-theme-hover;
}
/* 内容 */
.el-input__inner,
/* 日期选择 */
.el-range-input,
/* 日期图标 */
.el-range__icon,
/* 日期分隔 */
.el-range-separator
{
color: $color-white;
}
}
}
/* textarea输入框 */
.style-textarea {
/* 内容 */
.el-textarea__inner {
box-shadow: none;
color: $color-white;
border: $border-theme;
background: $bg-transparent;
&:hover,
&:focus {
border: $border-theme-hover;
}
}
}
/* select下拉框 */
/* 边框高亮直接给el-select 加上class="style-input" */
/* select框加背景图 */
.style-select-bg {
width: 100px;
.el-input__wrapper {
&:hover {
border: none;
background: url('../images/btn_hover.svg') 0 0 / 100% 100% no-repeat !important;
}
border-radius: 0;
border: none;
background: url('../images/btn_default.svg') 0 0 / 100% 100% no-repeat !important;
}
.el-input.is-focus .el-input__wrapper,
.el-input__wrapper.is-focus {
border: none;
box-shadow: unset !important;
background: url('../images/btn_hover.svg') 0 0 / 100% 100% no-repeat !important;
}
.el-input_inner {
color: $color-white;
}
}
/* select纯文字加渲染背景色 */
.style-select-text {
width: 130px;
.el-input__wrapper {
&:hover {
border: none;
background: #285757;
}
border-radius: 0;
border: none;
background: unset;
}
.el-input.is-focus .el-input__wrapper,
.el-input__wrapper.is-focus {
border: none;
box-shadow: unset !important;
background: #285757;
}
.el-input_inner {
color: $color-white;
}
}
/* radio单选框组 */
.style-radios {
/* 单选框 */
.el-radio {
/* 选中 */
&.is-checked {
/* 字体颜色 */
.el-radio__label {
color: $color-theme;
}
}
/* 圆圈 */
.el-radio__inner {
border: $border-radio;
background: $bg-gray;
}
}
}
/* switch开关 */
.style-switch {
/* 关闭 */
.el-switch__core {
background: $bg-switch;
border-color: $bg-switch;
}
/* 开启 */
&.is-checked .el-switch__core {
background: $bg-switch-active;
border-color: $bg-switch-active;
}
}
/* Input Number 数字输入框 */
/* el-input 加上class="style-input" */
.style-inputNumber {
/* 减 */
.el-input-number__decrease {
color: $color-white;
border-right: $border-theme;
background: $bg-transparent;
}
/* 增 */
.el-input-number__increase {
color: $color-white;
border-left: $border-theme;
background: $bg-transparent;
}
}
/* 单联date-picker日期选择器 */
/* 双联date-picker日期选择器 */
/* el-date-picker 加上class="style-input" */
.style-picker {
width: 200px;
}
/* drawer抽屉 */
.style-drawer {
width: 340px !important;
background: $bg-theme;
/* 头部 */
.el-drawer__header {
margin: 0;
padding: 24px;
border-bottom: $border-theme;
/* 删除按钮 */
.el-drawer__close-btn {
// display: none;
}
}
/* 内容 */
.el-drawer__body {
padding: 4px 24px;
}
/* 底部 */
.el-drawer__footer {
padding: 20px 24px;
border-top: $border-theme;
}
}
/* tag标签 */
.style-tag {
border-radius: 2px;
padding: 9px 8px;
background: $bg-theme;
color: $color-white;
&:hover {
background: $color-theme;
}
/* 选中 */
&.is-checked {
background: $bg-li-hover;
color: $color-white;
}
}
/* card卡片 */
.style-card {
.el-card__body {
padding: 10px 15px;
cursor: pointer;
}
}
/* Pagination 分页 */
/* el-pagination 加上class="style-input" */
.style-pagination {
/* 总数,跳转 */
.el-pagination__total,
.el-pagination__jump {
color: $color-title;
}
/* 页码,上下一页 */
li,
.btn-prev,
.btn-next {
color: $color-title;
background: transparent;
&:hover {
color: $color-theme;
}
}
/* 页码选中 */
.is-active {
background: $background-color-li-active;
color: $color-theme;
}
}
/* message box 弹窗 */
.el-message-box.style-message-box {
background: #090b1a;
border-color: #0d959a;
.el-message-box__message,
.el-message-box__title,
.el-message-box__close {
color: #fff;
}
.el-button {
border-radius: 0;
background: #0ff8f8;
color: #333;
&:hover {
background: #0dc9cc;
}
&:first-child {
background: transparent;
color: #fff;
border: 1px solid #323869;
&:hover {
color: #0dc9cc;
border: 1px solid #0dc9cc;
}
}
}
}
/* table表格 */
.style-table {
width: 100%;
height: 100px;
color: $color-white;
background: $bg-transparent;
/* 表格边框 */
--el-table-border-color: transparent;
/* 表格下边线 */
.el-table__inner-wrapper {
&::before {
display: none;
}
}
/* 表头 */
.el-table__header {
width: 100% !important;
tr {
background: $bg-transparent;
th {
border-bottom: none;
color: $color-white;
background: $bg-transparent;
}
}
}
/* 表格内容 */
.el-table__body {
width: 100% !important;
/* 行边距 */
-webkit-border-vertical-spacing: 2px;
tr {
background: transparent;
&.click-row {
td {
background: #276366 !important;
}
}
&.check-row {
td {
background: #0C4649 !important;
}
}
&:hover {
cursor: pointer;
td {
background: #09232D !important;
}
}
td {
border-bottom: none;
background: transparent;
}
}
}
/* 固定列 */
.el-table__header,
.el-table__body {
.el-table-fixed-column--right,
.el-table-fixed-column--left {
background: #080a19;
}
}
/* 复选框 */
.el-checkbox__inner {
background: transparent;
border: 1px solid #0A868C;
width: 14px;
height: 14px;
}
/* 复选框选中 */
.el-checkbox__input.is-checked,
.el-checkbox__input.is-indeterminate{
.el-checkbox__inner {
background: #0A868C;
border: 1px solid #0A868C;
}
}
}
}
/* popper弹窗 */
.popper-default {
border: $border-theme !important;
background: $bg-theme;
/* 箭头颜色背景 */
// .el-popper__arrow::before {
// background: $bg-theme;
// border-color: $color-theme;
// }
/* 箭头隐藏 */
.el-popper__arrow {
display: none;
}
/* select下拉框行 */
.el-select-dropdown__item {
color: $color-white;
padding: 0 12px;
&.hover,
&:hover {
background-color: $bg-li-hover;
}
}
/* dropdown下拉菜单行 */
.el-dropdown-menu__item {
font-size: 12px;
color: #000;
}
/* 单联date-picker日期选择器 */
/* 面板 */
.el-picker-panel {
background: $bg-theme;
color: $color-white;
}
/* 头部文字 */
.el-date-picker__header-label,
/* 左右按钮文字 */
.el-picker-panel__icon-btn,
/* 星期文字 */
.el-date-table th,
/* 月份文字 */
.el-month-table td .cell,
/* 月份文字 */
.el-year-table td .cell {
color: $color-white;
&:hover {
color: $color-blue;
}
}
/* 双联date-picker日期选择器 */
/* 内容右边框 */
.el-picker-panel__content {
border-right: $border-theme;
}
/* 选日期月份时范围背景色 */
.el-date-table td.in-range .el-date-table-cell,
.el-month-table td.in-range div,
.el-month-table td.in-range div:hover {
background: #2b2b2c;
}
/* 时间选择器 */
.el-picker-panel.has-time {
.el-picker-panel__footer {
background: $background-color-theme;
.el-button.is-text {
color: #fff;
&:hover {
background: none;
color: $color-theme;
}
}
.el-button.is-plain {
color: #333;
border: none;
background: $color-theme-hover;
&:hover {
background: $color-theme;
}
}
}
.el-picker-panel__body {
.el-input__wrapper {
background: $background-color-theme;
.el-input__inner {
color: #fff;
}
}
.el-time-panel {
background: $background-color-theme;
}
}
}
/* menu菜单 */
.style-menu {
padding: 4px 4px 0;
/* 鼠标移入 */
.el-menu-item:hover {
color: #409eff !important;
}
/* 选中 */
.el-menu-item.is-active {
color: #165dff;
font-weight: 550;
background: #e8f3ff;
}
/* 一级菜单下拉菜单标题 */
&>.el-menu-item,
.el-sub-menu__title {
margin-bottom: 4px;
height: 38px;
line-height: 38px;
}
/* 下拉箭头 */
.el-sub-menu__icon-arrow {
right: 10px;
font-size: 16px;
}
/* 下拉菜单子项 */
.el-sub-menu .el-menu-item {
margin-bottom: 4px;
padding-left: 20px !important;
padding-right: 0 !important;
height: 32px;
line-height: 32px;
}
}
/* checkbox 多选框 */
.style-checkbox {
/* 每个选框单独一行 */
display: block;
/* 字体颜色 */
--el-checkbox-checked-text-color: #606266;
/* 选框颜色 */
:deep(.el-checkbox__input.is-checked),
/* 全选框颜色 */
:deep(.el-checkbox__input.is-indeterminate) {
.el-checkbox__inner {
background: #165dff;
border: 1px solid #165dff;
}
}
}
}
/* dialog弹窗 */
.dialog-default {
border: $border-purple;
background: $bg-theme;
/* 头部 */
.el-dialog__header {
padding: 16px;
border-bottom: $border-purple;
text-align: center;
/* 标题 */
.el-dialog__title {
color: $color-white;
font-size: $font-size-16;
font-weight: $font-weight;
}
/* 删除按钮 */
.el-dialog__headerbtn {
top: 8px;
right: 10px;
font-size: 20px;
width: auto;
height: auto;
i {
padding: 5px;
}
& :hover {
color: $color-white;
}
}
}
/* 内容 */
.el-dialog__body {
padding: 25px 20px;
}
/* 底部 */
.el-dialog__footer {
border-top: $border-purple;
padding: 16px 20px;
text-align: right;
}
}
/* 消息box */
.el-message-box {
/* 标题 */
.el-message-box__title {}
/* 内容 */
.el-message-box__content {
text-align: left;
color: #1D2129;
}
/* 按钮 */
.el-message-box__btns {
justify-content: flex-end;
.el-button.el-button--primary {
background: #f53f3f;
border: none;
}
}
}
表格+分页
<template>
<div class="element-style">
<!-- table表格 -->
<el-table class="style-table" :style="width: 100%"
:data="tableData" size="small" :row-class-name="rowClass" @row-click="handleRowClick" @selection-change="selectionChange">
<!-- 复选框 -->
<el-table-column type="selection" fixed="left" width="55" />
<!-- 序号 -->
<el-table-column prop="index" fixed="left" label="序号" width="50" />
<!-- 遍历表格列 -->
<el-table-column v-for="item in tableColunm" :key="item.prop" :label="item.label" :width="item.width"
table-layout="auto" :align="item.prop == 'level' ? 'center' : 'left'" show-overflow-tooltip>
<template #default="scope">
<!-- 工单编号 -->
<div v-if="item.prop == 'code'">
{{ scope.row[item.prop] }}
<el-button v-if="scope.row.index < 5" color="#093a46" size="small"
style="color: #0ff8f8; margin-left: 5px">最新</el-button>
</div>
<!-- 带颜色按钮 -->
<div v-else-if="item.prop == 'level'" :style="{
display: 'inline-block',
padding: '1px 8px',
borderRadius: '2px',
color: getLevelColor(scope.row[item.prop], 'color'),
background: getLevelColor(scope.row[item.prop], 'bg'),
}">
{{ scope.row[item.prop] }}
</div>
<div v-else>{{ scope.row[item.prop] }}</div>
</template>
</el-table-column>
<!-- 操作 -->
<el-table-column align="center" label="操作" width="100" fixed="right">
<template #default="scope">
<el-popover popper-class="table-popover" placement="bottom" :width="140" trigger="hover">
<template #reference>
<div class="table-operate">...</div>
</template>
<div v-for="item in tableOperate" :key="item.value" class="popover-item"
@click="() => handleOperate(scope.row, item)">
{{ item.label }}
</div>
</el-popover>
</template>
</el-table-column>
</el-table>
<!-- Pagination 分页 -->
<el-pagination class="style-pagination" :current-page="pageNum" :page-size="pageSize"
popper-class="popper_default" :page-sizes="[20, 30, 40, 50]" :background="true"
layout="total, prev, pager, next, sizes, jumper" :total="totalSize" @size-change="handleSizeChange"
@current-change="handleCurrentChange">
</el-pagination>
</div>
</template>
<script setup lang='ts'>
// 表格行高亮
const rowHighlight = ref('');
// 表格分页页码
const pageNum = ref(1);
// 表格分页页数
const pageSize = ref(20);
// 表格分页总数
const totalSize = ref(50);
// 表格多选项
const multipleSelection = ref([]);
// 表格列
const tableColunm = ref<ITableColunm[]>([
{ prop: 'code', label: TableEnum.OrderCode, width: '200' },
{ prop: 'level', label: TableEnum.UrgencyDegree },
{ prop: 'type', label: TableEnum.BusinessType },
{ prop: 'subType', label: TableEnum.BusinessSub },
{ prop: 'user', label: TableEnum.AppealAndPhone },
{ prop: 'time', label: TableEnum.DealTime },
{ prop: 'content', label: TableEnum.CallContent },
]);
// 表格数据
const tableData = [
{
index: 1,
code: '202201328976543',
level: '12398',
type: '客户投诉',
subType: '停电/停电故障',
powerType: '商业用电',
user: '李明博 - 177 9876 5678',
userCode: '李达康 - 20837898689692',
time: '2022-12-09 22:25:33',
content: '一户停电,需要工作人员到场协助开启变电箱。核实处理。',
},
{
index: 2,
code: '202201328976543',
level: '督办',
type: '客户投诉',
subType: '停电/停电故障',
powerType: '商业用电',
user: '李明博 - 177 9876 5678',
userCode: '李达康 - 20837898689692',
time: '2022-12-09 22:25:33',
content: '一户停电,需要工作人员到场协助开启变电箱。核实处理。',
},
{
index: 3,
code: '202201328976543',
level: '一般',
type: '客户投诉',
subType: '停电/停电故障',
powerType: '商业用电',
user: '李明博 - 177 9876 5678',
userCode: '李达康 - 20837898689692',
time: '2022-12-09 22:25:33',
content: '一户停电,需要工作人员到场协助开启变电箱。核实处理。',
},
{
index: 4,
code: '202201328976543',
level: '12398',
type: '客户投诉',
subType: '停电/停电故障',
powerType: '商业用电',
user: '李明博 - 177 9876 5678',
userCode: '李达康 - 20837898689692',
time: '2022-12-09 22:25:33',
content: '一户停电,需要工作人员到场协助开启变电箱。核实处理。',
},
{
index: 5,
code: '202201328976543',
level: '12398',
type: '客户投诉',
subType: '停电/停电故障',
powerType: '商业用电',
user: '李明博 - 177 9876 5678',
userCode: '李达康 - 20837898689692',
time: '2022-12-09 22:25:33',
content: '一户停电,需要工作人员到场协助开启变电箱。核实处理。',
},
];
/**
* 表格选中行
* @param {type} 参数
* @returns {type} 返回值
*/
const handleRowClick = (val) => {
orderStore.$patch((state) => {
state.workNo = val.workNo;
state.showOrderDetail = true;
state.showOrderCard = false;
state.refreshUserInfo = true;
});
rowHighlight.value = val.workNo || '';
};
/**
* 表格多选框
* @param {type} 参数
* @returns {type} 返回值
*/
const selectionChange = (val) => {
multipleSelection.value = val;
};
/**
* 选中行样式
* @param {type} 参数
* @returns {type} 返回值
*/
const rowClass = ({ row, rowIndex }) => {
// 勾选高亮
if (multipleSelection.value.find((item: any) => item.workNo === row.workNo)) {
return 'check-row';
}
// 点击高亮
if (rowHighlight.value && row.workNo === rowHighlight.value) {
return 'click-row';
}
};
/**
* 表格分页页码改变
* @param {number} val 页码
*/
const handleCurrentChange = (val: number) => {
pageNum.value = val;
}
/**
* 表格分页页数改变
* @param {number} val 页码
*/
const handleSizeChange = (val: number) => {
pageSize.value = val;
};
/**
* 获取表格紧急程度颜色
* @param {type} 参数
* @returns {type} 返回值
*/
const getLevelColor = (value: string, type: string) => {
// 表格紧急程度颜色
const levelColor = [
{ name: '12398', color: '#F53F3F', bgColor: '#371824' },
{ name: '投诉', color: '#F53F3F', bgColor: '#371824' },
{ name: '督办', color: '#FF7D00', bgColor: '#392419' },
{ name: '紧急', color: '#FF7D00', bgColor: '#392419' },
{ name: '预警', color: '#FF7D00', bgColor: '#392419' },
{ name: '一般', color: '#165DFF', bgColor: '#09173c' },
];
const obj = levelColor.find((item) => item.name == value);
if (type == 'color') {
return obj?.color;
}
return obj?.bgColor;
};
/**
* 清除表格行高亮
* @param {type} 参数
* @returns {type} 返回值
*/
const clearRowBg = () => {
if (rowHighlight.value) {
rowHighlight.value = '';
}
};
</script>
遍历生成筛选输入下拉框
<template>
<div class="element-style">
<!-- 输入框遍历 -->
<div class="input-filter">
<div v-for="item in formData" :key="item.name" class="filter-item">
<div class="item-name" v-if="item.name">{{ item.name }}</div>
<div class="item-form">
<!-- 下拉框 -->
<el-select v-if="item.type === 'select'" v-model="item.value" popper-class="popper-default"
class="style-select" :placeholder="item.placeholder || '请选择'" size="default"
@change="(val) => valueChange(item.type, val)">
<el-option v-for="value in item.options" :key="value.value" :label="value.label"
:value="value.value" />
</el-select>
<!-- 单选框 -->
<el-radio-group v-else-if="item.type === 'radio'" class="style-radios" v-model="item.value" @change="(val) => valueChange(item.type, val)">
<el-radio v-for="(value, i) in item.radio" :key="value" :label="i">{{ value }}</el-radio>
</el-radio-group>
<!-- 单联日期选择器 -->
<el-date-picker v-else-if="item.type === 'dataPicker'" class="style-input style-picker" v-model="item.value" @change="(val) => valueChange(item.type, val)"
popper-class="popper-default" value-format="YYYY-MM-DD" type="date"
:placeholder="item.placeholder" size="default" />
<!-- 双联日期选择器 -->
<el-date-picker v-else-if="item.type === 'dataPickers'" class="style-input style-picker" v-model="item.value" @change="(val) => valueChange(item.type, val)"
popper-class="elDatePicker" value-format="YYYY-MM-DD" type="daterange" range-separator="To"
start-placeholder="开始日期" end-placeholder="结束日期" size="default" @change="dateChange" />
<!-- textarea输入框 -->
<el-input v-else-if="item.type === 'textarea'" v-model="item.textareaValue" :rows="item.rows || 1" @change="(val) => valueChange(item.type, val)"
class="style-input" type="textarea" :placeholder="item.placeholder" />
<!-- input输入框 -->
<el-input v-else-if="item.type === 'input'" v-model="item.value" class="style-textarea" @change="(val) => valueChange(item.type, val)"
:placeholder="item.placeholder" ></el-input>
</div>
</div>
<slot></slot>
</div>
</div>
</template>
<script setup lang="ts">
import { watch } from 'vue';
const emits = defineEmits<{
(e: "change", key: String, value: any,): void;
}>()
// [{ name: '类型', key: 'channelType', value: '', defaultValue: 'all', type: 'select', option: [ { label: '全部', value: 'all' }, { label: '其他', value: '3' }, ] }]
const props = defineProps<{ formData }>();
watch(() => props.formData, (val) => {
val.forEach(v => {
if (v.defaultValue) {
v.value = v.defaultValue
}
})
}, { immediate: true })
/**
* 表单数据改变
*/
const valueChange = (key, value) => { emits("change", key, value); }
</script>
<style scoped lang="scss">
.element-style {
.input-filter {
@include flex(5);
.customer-item {
margin-bottom: 25px;
width: 30%;
.item-name {
margin-bottom: 15px;
}
}
}
}
</style>
upload上传
<template>
<div class="element-style">
<!-- 上传excel -->
<el-upload
ref="uploadRef"
class="style-upload"
:data="uploadData"
:auto-upload="false"
accept=".xlsx, .xls"
:on-change="handleChange"
:on-success="handleSuccess"
:on-error="handleError"
:on-exceed="handleExceed"
:before-remove="beforeRemove"
:action="actionUrl"
:limit="1"
:file-list="excelList"
>
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
<div slot="tip" class="el-upload__tip">只能上传.xls/.xlsx文件</div>
</el-upload>
<!-- upload图片上传 -->
<el-upload
class="style-upload"
multiple
action=""
accept=".png, .jpg, .jpeg"
:on-remove="handleImgRemove"
:on-change="onImgChange"
:file-list="uploadImg"
:auto-upload="false"
>
<el-button size="small" type="primary">批量上传图片</el-button>
<div slot="tip" class="el-upload__tip">上传图片名称需与表格图片名称对应, 图片大小4mb以内</div>
</el-upload>
<el-button type="success" size="small" @click="handleSubmit">上传到服务器</el-button>
<!-- Collapse 折叠面板 -->
<!-- Tree 树形控件 -->
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ElMessage, ElLoading } from 'element-plus';
const loading = ref();
const imgActionUrl = window.uploadUrl + '/batchupload';
const actionUrl = window.uploadUrl + 'import';
const uploadImg = ref<any[]>([]); // 图片列表
const excelList = ref<any[]>([]); // 上传文件
const uploadImgSuccessList = ref<any[]>([]); //图片上传成功列表
const uploadRef = ref<any>(null);
// 上传携带参数
const uploadData = computed(() => {
return {};
});
/**
* 上传change
* @param {type} 参数
* @returns {type} 返回值
*/
function handleChange(file, fileList) {
// ...
excelList.value = fileList;
}
/**
* 上传成功
* @param {Object} response 响应
* @param {Object} file 文件
* @param {Object} fileList 文件夹
*/
function handleSuccess(response, file, fileList) {
loading.value.close();
ElMessage({ offset: 100, center: true, message: `上传成功`, type: 'success' });
}
/**
* 上传错误
* @param {string} err 错误
* @param {Object} file 文件
* @param {Object} fileList 文件夹
*/
function handleError(err, file, fileList) {
if (err) {
loading.value.close();
ElMessage({ offset: 100, center: true, message: `excel上传失败, 请重试`, type: 'error' });
}
}
/**
* 文件超出数量上限
* @param {Object} file 文件
* @param {Object} fileList 文件夹
*/
function handleExceed(files, fileList) {
ElMessage({
offset: 100,
center: true,
message: `当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`,
type: 'warning',
});
}
/**
* 删除上传文件
* @param {type} 参数
* @returns {type} 返回值
*/
function beforeRemove(file, fileList) {
excelList.value = [];
return true;
}
/**
* 处理图片移除
* @param {Object} file 文件
* @param {Object} fileList 文件夹
*/
function handleImgRemove(file, fileList) {
uploadImg.value = [...fileList];
}
/**
* 图片改变
* @param {Object} file 文件
* @param {Object} fileList 文件夹
*/
function onImgChange(file, fileList) {
if (file.status === 'success') {
return;
}
const types = ['image/jpeg', 'image/png', 'image/jpg'];
const isJPG = types.includes(file.raw.type);
const isLt2M = file.size / 1024 / 1024 < 4;
if (!isJPG) {
ElMessage({ offset: 100, center: true, message: '图片只能是 JPG/PNG/JPEG 格式!', type: 'error' });
const index = fileList.map((item) => item.uid).indexOf(file.uid);
fileList.splice(index, 1);
}
if (!isLt2M) {
ElMessage({ offset: 100, center: true, message: '图片大小不能超过 4MB!', type: 'error' });
const index = fileList.map((item) => item.uid).indexOf(file.uid);
fileList.splice(index, 1);
}
if (uploadImg.value.length) {
if (uploadImg.value.map((item) => item.name).includes(file.name)) {
ElMessage({ offset: 100, center: true, message: '图片重复,已移除!', type: 'error' });
const index = fileList.map((item) => item.uid).indexOf(file.uid);
fileList.splice(index, 1);
}
}
uploadImg.value = [...fileList];
}
/**
* 上传图片
*/
async function uploadImgFn() {
if (uploadImg.value.length === 0) {
return;
}
const formData = new FormData();
uploadImg.value.forEach((item) => {
formData.append('files', item.raw);
});
formData.append('isNeedSync', '1');
const res = await fetch(imgActionUrl, { method: 'POST', body: formData }).then((response) => response.json());
if (res && res.code === 2000) {
uploadImgSuccessList.value = res.data;
}
}
/**
* 提交上传
* @param {type} 参数
* @returns {type} 返回值
*/
async function handleSubmit() {
//加载中提示
loading.value = ElLoading.service({
lock: true,
text: '上传数据中,请稍等...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)',
});
await uploadImgFn();
uploadRef.value.submit();
}
</script>
<style scoped lang="scss">
.element-style {
/* upload上传 */
.style-upload {
margin-top: 20px;
.el-upload-list {
max-height: 200px;
overflow-y: auto;
}
}
}
</style>
collapse 折叠面板
<template>
<div class="element-style">
<!-- checkbox 多选框 -->
<!-- Collapse 折叠面板 -->
<el-collapse
v-for="(value, key) in collapseList"
:key="key"
v-model="activeNames"
class="style-collapse"
accordion
@change="handleChange"
>
<el-collapse-item :name="key">
<template #title>
<div>{{ key }}</div>
<!-- 操作点 -->
<el-popover placement="bottom-start" :visible-arrow="false" width="100" trigger="click">
<i slot="reference" style="font-size: 20px" @click.stop>...</i>
<!-- 编辑按钮 -->
<div class="el-icon-edit list-item-edit" @click.stop="handleEdit(key)"> 重命名</div>
<!-- 删除按钮 -->
<div class="el-icon-delete list-item-edit" @click.stop="handleDel(key)"> 删除</div>
<!-- 点 -->
</el-popover>
</template>
<template v-if="showCollapse"> 内容 </template>
</el-collapse-item>
</el-collapse>
<!-- Tree 树形控件 -->
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const collapseList = ref([]);
const showCollapse = ref(true);
const activeNames = ref(''); // 折叠面板选中项
/**
* 折叠面板切换事件
* @param {string} val
*/
function handleChange(val) {
if (!val) {
return;
}
if (!collapseList.value[activeNames.value]) {
showCollapse.value = false;
} else {
showCollapse.value = false;
showCollapse.value = true;
}
}
</script>
<style scoped lang="scss">
.element-style {
/* Collapse 折叠面板 */
.style-collapse {
border-top: none;
.el-collapse-item__header {
position: relative;
padding-left: 15px;
height: 38px;
line-height: 38px;
display: flex;
justify-content: space-between;
.el-collapse-item__arrow {
position: absolute;
left: 0;
}
}
.el-collapse-item__content {
padding-bottom: 0;
}
}
}
</style>
tree 树形结构
<template>
<div class="element-style">
<!-- Tree 树形控件 -->
<el-card class="main-card">
<el-input v-model="filterText" class="card-input" placeholder="请输入文本" suffix-icon="el-icon-search"></el-input>
<div class="card-tree">
<el-tree
ref="treeRef"
lazy
:data="treeList"
:load="loadNode"
:props="defaultProps"
highlight-current
node-key="id"
:filter-node-method="filterNode"
@node-click="clickNode"
>
<!-- 树形内容 -->
<template #default="{ node, data }">
<div class="tree-content">
<div class="content-text">
<!-- 文件夹图标 -->
<i v-show="data.type == 0" class="text-icon mzIconfont icon-file"></i>
<!-- 杆塔图标 -->
<i v-show="data.type != 0" class="mzIconfont icon-tower text-tower"></i>
<!-- 名称 -->
<el-tooltip
v-if="data.folderName && data.folderName.length > 10"
effect="dark"
:content="data.folderName || data.name"
placement="top"
>
<span>{{ data.folderName || data.name }}</span>
</el-tooltip>
<span v-else>{{ data.folderName || data.name }}</span>
</div>
<div class="content-icon">
<!-- 显示隐藏元素 -->
<i
v-if="data.type == 0 && data.status !== undefined"
:class="['mzIconfont', data.status ? 'icon-eye' : 'icon-no_eye']"
@click.stop="openFn(node, data)"
></i>
<!-- 关联按钮 -->
<i v-if="data.type == 0" class="mzIconfont icon-guanlian1" @click.stop="linkFn(data)"></i>
</div>
</div>
</template>
</el-tree>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
const isSearch = ref(false); // 搜索状态
const treeList = ref<any[]>([]); //树形数据
const filterText = ref(''); //过滤关键词
const treeTimer = ref<any>(null); //搜索延时器
const treeRef = ref<any>(null);
//树形数据默认结构
const defaultProps = ref({
children: 'children',
label: 'folderName',
isLeaf: 'isLeaf',
});
watch(filterText, async (newVal, oldVal) => {
// treeRef.value!.filter(val);
// 清除延时器
if (treeTimer.value) {
clearTimeout(treeTimer.value);
treeTimer.value = null;
}
// 空值
if (!newVal) {
//非搜索类型
isSearch.value = false;
// 重新请求第一层数据
const res = await postFolderTreeAPI({});
res[0].children.splice(0, 5);
treeList.value = setStatus([...res[0].children], true);
return;
}
// 是搜索类型
isSearch.value = true;
treeTimer.value = setTimeout(async () => {
//模糊搜索请求
const res = await postFolderSearchAPI({});
// 要素类型isLeaf改为true
const portalDataSources = res[0].portalDataSources;
portalDataSources &&
portalDataSources.forEach((item) => {
item.isLeaf = true;
});
// 渲染到tree
treeList.value = [...res[0].children, ...portalDataSources].splice(0, 30);
}, 500);
});
/**
* 加载节点
* @param {type} 参数
* @returns {type} 返回值
*/
function loadNode(node, resolve) {
//如果展开第一级节点,从后台加载一级节点列表
if (node.level === 0) {
loadfirstnode(resolve);
}
//如果展开其他级节点,动态从后台加载下一级节点列表
if (node.level >= 1) {
loadchildnode(node, resolve);
}
}
/**
* 加载第一级节点
* @param {type} 参数
* @returns {type} 返回值
*/
async function loadfirstnode(resolve) {
const res = await postFolderTreeAPI({});
res[0].children.splice(0, 5);
return resolve(setStatus([...res[0].children], true));
}
/**
* 加载节点的子节点集合
* @param {type} 参数
* @returns {type} 返回值
*/
async function loadchildnode(node, resolve) {
// console.log("超过二级的", node, node.level);
const res = await postFolderTreeAPI({});
// 1.节点children不为空,portalDataSources不为空
if (res[0].children.length > 0 && res[0].portalDataSources.length > 0) {
const data = res[0].portalDataSources;
data &&
data.forEach((item) => {
item.isLeaf = true;
});
const children = setStatus(res[0].children, true);
children.push(...data);
return resolve(children);
}
// 2.节点children不为空,portalDataSources为空
if (res[0].children.length > 0 && res[0].portalDataSources.length == 0) {
return resolve(setStatus(res[0].children, true));
}
// 3.节点children为空,portalDataSources不为空
if (res[0].children.length == 0 && res[0].portalDataSources.length > 0) {
const data = res[0].portalDataSources;
data &&
data.forEach((item) => {
item.isLeaf = true;
});
return resolve(data);
}
}
// 给子节点设置状态
function setStatus(data, status) {
// 非搜索类型才加状态
if (!isSearch.value) {
data.forEach((item) => {
item.status = status;
});
}
return data;
}
/**
* 过滤数据
* @param {type} 参数
* @returns {type} 返回值
*/
const filterNode = (value: string, data) => {
if (!value) {
return true;
}
return data.label.includes(value);
};
/**
* 点击树形节点
* @param {type} 参数
* @returns {type} 返回值
*/
async function clickNode(data, node) {}
</script>
<style scoped lang="scss">
.element-style {
/* Collapse 折叠面板 */
.style-collapse {
border-top: none;
.el-collapse-item__header {
position: relative;
padding-left: 15px;
height: 38px;
line-height: 38px;
display: flex;
justify-content: space-between;
.el-collapse-item__arrow {
position: absolute;
left: 0;
}
}
.el-collapse-item__content {
padding-bottom: 0;
}
}
}
</style>
el-form表单
<template>
<div class="element-style">
<!-- el-form表单 -->
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" class="style-form">
<el-form-item prop="telephone">
<div class="item-title">账号</div>
<el-input class="style-input" v-model="ruleForm.telephone" size="large" type="text" />
</el-form-item>
<el-form-item prop="password">
<div class="item-title">密码</div>
<el-input class="style-input" v-model="ruleForm.password" size="large" :type="showPass ? 'text' : 'password'" />
<el-icon @click="showPassword"><Hide v-if="!showPass" /><View v-else /></el-icon>
</el-form-item>
<el-form-item>
<el-button size="large" color="#0FF8F8" @click="login(ruleFormRef)">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup lang="ts">
// 显示密码
const showPass = ref(false);
// 表单ref
const ruleFormRef = ref<FormInstance>();
// 验证码图片
const authCode = ref({
key: '',
image: '',
});
// 表单字段
const ruleForm = reactive({
password: '',
telephone: '',
code: '',
});
// 表单校验
const rules = reactive<FormRules>({
telephone: [{ required: true, message: '请输入账号', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
code: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
});
/**
* 获取验证码
*/
const getAuthCode = async () => {
try {
const res: any = await UserAPI.getAuthCode();
authCode.value = res;
} catch (err: any) {
console.log(err);
}
};
/**
* 登录
* @param {FormInstance} formEl
*/
const login = async (formEl: FormInstance | undefined) => {
}
/**
* 显示密码
*/
const showPassword = () => {
showPass.value = !showPass.value;
};
</script>
<style lang="scss">
.element-style {
.style-form {
.el-form-item {
margin-bottom: vh(45);
.el-form-item__label {}
.el-form-item__content {
.el-input {
/* 容器 */
.el-input__wrapper {
padding-left: 60px;
border-bottom: 1px solid #5B66BD;
/* 内容 */
.el-input__inner {
font-size: 16px;
}
}
}
.el-icon {
position: absolute;
right: 17px;
top: 10px;
font-size: 16px;
color: #4e5969;
cursor: pointer;
}
}
}
}
}
</style>
radio group 生成 tab
<el-radio-group @change="onTabChange" size="large" v-model="tabValue">
<el-radio-button label="日"></el-radio-button>
<el-radio-button label="月"></el-radio-button>
</el-radio-group>
.el-radio-group {
padding: 3px;
padding-right: 0;
background: #f1f2f5;
/* 中间线 */
.el-radio-button {
margin-right: 3px;
--el-radio-button-checked-border-color: transparent !important;
/* 按钮 */
.el-radio-button__inner {
border: none !important;
background: unset;
}
/* 悬浮 */
&:hover .el-radio-button__inner {
background: #fff;
color: #333;
}
/* 选中 */
&.is-active .el-radio-button__inner {
background: #fff;
color: #0052d9;
}
}
}
<el-radio-group v-model="radioValue" size="mini">
<el-radio-button label="medium">中压疑似停电</el-radio-button>
<el-radio-button label="low">低压疑似停电</el-radio-button>
</el-radio-group>
.el-radio-button {
.el-radio-button__inner {
border-radius: 14px 0 0 14px;
}
&:last-child .el-radio-button__inner {
border-radius: 0 14px 14px 0;
box-shadow: -1px 0 0 0 #BCD9FC;
}
&.is-active .el-radio-button__inner {
background-color: #eff5fe;
border-color: #BCD9FC;
color: #006bff;
}
}
下拉框加 loading 状态
el-cascader不支持v-loading el-select :loading效果差 需要正常的动画 loading 状态
实现方案
利用 服务方法 ElLoading, 它支持传dom的类名
实现代码
import { ElLoading } from 'element-plus';
import { ref, nextTick } from "vue";
/**
* 添加加载状态
*/
export const useLoading = () => {
const cascaderLoading = ref(null);//加载动画
return {
cascaderLoading,
showLoading: (className: string) => {
let options = {
target: className,//你自定义的类名
lock: true,
text: '加载中...'
};
nextTick(() => {
cascaderLoading.value = ElLoading.service(options);
});
},
closeLoading: () => cascaderLoading.value?.close();
}
}
- 使用
<el-cascader popper-class="cascader-loading"v-model="cascaderData" :options="cascaderOptions" @change="cascaderChange" :props="optionProps" clearable />
import { useLoading } from '@/utils'
const { showLoading, closeLoading } = useLoading()
const requestCascaderData = ()=> {
showLoading('.cascader-loading')
//....
closeLoading()
}
带加载状态的消息弹窗
要实现一个类似element的ElMessage但是带加载状态的效果, ElMessage上加的图标默认是静态的, 所以仿ElMessage写一个组件, 带动画, 支持加载状态
实现步骤
- 利用el-button可以带loading效果的特性, 实现loading动态图标
<el-button text loading>正在校验...</el-button>
- 容器和居中定位(注意水平居中定位不能用 transform: translate, 会影响动画用的transform: translate)
<div v-if="uploadStatus === 1" class="content-checkout">
<el-button text loading>正在校验...</el-button>
</div>
.content-checkout {
position: absolute;
top: -130px;
left: 50%;
margin-left: -80px;
padding: 8px 16px 8px 8px;
border-radius: 5px;
background: #fff;
.el-button {
color: #0052d9;
:deep(.el-icon) {
font-size: 18px;
}
}
}
- 利用vue的动画组件Transition实现顶部移入移出动画效果
<Transition name="slide-fade">
...
</Transition>
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: all 0.5s;
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateY(-50px);
}
实现代码
<Transition name="slide-fade">
<div v-if="uploadStatus === 1" class="content-checkout">
<el-button text loading>正在校验...</el-button>
</div>
</Transition>
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: all 0.5s;
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateY(-50px);
}
.content-checkout {
position: absolute;
top: -130px;
left: 50%;
margin-left: -80px;
padding: 8px 16px 8px 8px;
border-radius: 5px;
background: #fff;
.el-button {
color: #0052d9;
:deep(.el-icon) {
font-size: 18px;
}
}
}
轮播图+ 预览+ 上传+ 删除
实现轮播图能够预览, 还支持上传和删除图片的功能 html
<!-- 轮播图 -->
<el-carousel :initial-index="1" trigger="click" height="180px" :autoplay="false">
<el-carousel-item style="text-align:center" v-for="(item, index) in urlList" :key="item">
<!-- 图片 -->
<img @mouseenter="handleMouseenter(index)" style="height:100%" :src="item" alt="">
<!-- 遮罩层 -->
<div v-show="activeIndex == index" @mouseout="handleMouseleave"
style="position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);display:flex;justify-content: center;align-items: center;color:#fff;font-size:24px">
<!-- 打开大图 -->
<i @click="openImg(item)" style="margin-right:20px;cursor:pointer" class="el-icon-zoom-in"></i>
<!-- 删除图片 -->
<i @click="delImg(index)" style="cursor:pointer" class="el-icon-delete"></i>
</div>
</el-carousel-item>
<!-- 新增图片 -->
<el-carousel-item>
<div style="margin-top:18px;text-align:center">
<el-upload :data="uploadData" :action="uploadUrl" list-type="picture-card" :show-file-list="false"
:on-success="handleSuccess">
<i class="el-icon-plus"></i>
</el-upload>
</div>
</el-carousel-item>
</el-carousel>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
js
data() {
return {
activeIndex: null, // 当前下标
dialogVisible: false, // 显示大图
dialogImageUrl: '', // 大图路径
urlList: [], // 图片路径
files: [], // 图片资源
uploadUrl: window['uploadUrl'] + '/thp/filesvrapi/v1/fileservice/singleupload',
uploadData: { isNeedSync: 1 }
}
},
watch: {
activedItem: {
handler(val) {
this.urlList = [];
let files = []
if (val.files.length > 0) {
files= JSON.parse(JSON.stringify(val.files))
}
this.files = files
if (files.length > 0) {
this.getImgUrl(files)
}
},
deep: true
}
},
methods: {
/**
* 图片上传成功
* @param {type} 参数
* @returns {type} 返回值
*/
handleSuccess(res) {
const file = res.data
// 更新图片展示
this.getImgUrl([file])
// 更新图片数据
this.files.push(file)
// 更新项目详情
// this.updateDetail()
},
/**
* 更新项目详情
* @param {type} 参数
* @returns {type} 返回值
*/
async updateDetail() {
const data = JSON.parse(JSON.stringify(this.activedItem))
data.files = this.files
const res = await postWellImgUpdate(data)
if (res && res.code == 2000) {
this.$message({ offset: 100, center: true, message: '删除成功', type: 'success' })
}
},
/**
* 删除图片
* @param {number} index
*/
delImg(index) {
this.$confirm(`确定移除该图片?`).then(() => {
// 删除路径
this.urlList.splice(index, 1);
// 删除文件服务图片
const id = this.files[index] ? this.files[index].id : '';
this.handleFilesDel(id)
// 删除详情图片
this.files.splice(index, 1)
// 更新项目详情
// this.updateDetail()
})
},
/**
* 文件服务删除
* @param {string} ids
*/
handleFilesDel(ids) {
if (!ids) return
fetch(`${window['uploadUrl']}/thp/filesvrapi/v1/fileservice/delete`, {
method: 'post', headers: {
'Content-Type': 'application/json'
}, body: JSON.stringify({
"fileIds": ids
})
})
},
/**
* 打开大图
* @param {string} url
*/
openImg(url) {
this.dialogImageUrl = url;
this.dialogVisible = true;
},
/**
* 鼠标移入图片
* @param {number} index 下标
*/
handleMouseenter(index) {
this.activeIndex = index
},
/**
* 鼠标移出图片
* @param {number} index 下标
*/
handleMouseleave() {
this.activeIndex = null
},
/**
* 请求图片路径
* @param {[]} files
*/
getImgUrl(files) {
files.forEach(item => {
fetch(`${window['uploadUrl']}/thp/filesvrapi/v1/fileservice/singledownload?fileId=` + item.id, {
method: 'get',
responseType: 'blob'
}).then(res => res.blob()).then(blob => {
const url = window.URL.createObjectURL(new Blob([blob], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }));
this.urlList.push(url)
})
})
}
}
封装element-ui 的表格, columns 改成配置式
支持传入column配置, 支持render方法, 以prop为名的命名插槽 /src/components/Table.vue
<el-table class="gTable" :data="$attrs.data" style="width: 100%" v-bind="$attrs" v-on="$listeners">
<template v-for=" (item, index) in $attrs.columns ">
<!-- 复选框 -->
<el-table-column v-if="item.prop === 'selection'" :type="item.prop" v-bind="item"></el-table-column>
<!-- 序号 -->
<el-table-column v-else-if="item.prop === 'index'" :type="item.prop" v-bind="item"></el-table-column>
<el-table-column v-else v-bind="item">
<template slot-scope="scope">
<!-- render方法 -->
<div v-if="item.customRender" v-html="customRender(scope.row)"></div>
<!-- prop为名的插槽 -->
<div v-else-if="$scopedSlots[item.prop]">
<slot :name="item.prop" v-bind="scope.row"></slot>
</div>
<!-- 文本 -->
<div v-else>{{ scope.row[item.prop] }}</div>
</template>
</el-table-column>
</template>
</el-table>
- 使用
<Table border :data="tableData" :columns="tableColumns">
<template v-slot:operate="{ row }">
<el-button>查看</el-button>
</template>
</Table>
import { Table } from '/@/components/index.ts'
const tableData = [
{
no: '040500400183445',
area: '040500400183445',
lineName: '馈线名称',
pbName: '宜州市怀远镇罗山村',
type: '问题类型',
typeDetail: '问题类型明细',
from: '问题来源',
}
]
const tableColumns = [
{ prop: 'index', label: '序号' },
{ prop: 'no', label: '问题编码' },
{ prop: 'area', label: '区域名称' },
{ prop: 'lineName', label: '馈线名称' },
{ prop: 'pbName', label: '配变名称' },
{ prop: 'type', label: '问题类型' },
{ prop: 'typeDetail', label: '问题类型明细' },
{ prop: 'from', label: '问题来源' },
{ prop: 'operate', label: '操作' },
]
步骤条关联标题, 实现点击步骤滚动到标题位置
使用了el-step步骤条组件, 但是组件不支持click事件, 加个容器, click事件冒泡到容器 点击事件中修改activeStep值, 动态改选中step 获取标题元素, 利用dom 的scrollIntoView方法滚动内容
<!-- 左边-步骤条 -->
<div class="container-steps" @click="clickStep">
<el-steps space="60px" direction="vertical" :active="activeStep">
<el-step style="cursor: pointer" v-for="step in steps" :key="step" :title="step"></el-step>
</el-steps>
</div>
<!-- 左边-信息 -->
<div class="container-program">
<!-- 方案基础信息 -->
<div class="program-title" @click="clickStep">
{{ steps[0] }}
</div>
<!-- 中压部分 -->
<div class="program-title" @click="clickStep">
{{ steps[1] }}
</div>
<div style="height: 300px"></div>
<!-- 低压部分 -->
<div class="program-title" @click="clickStep">
{{ steps[2] }}
</div>
<div style="height: 300px"></div>
<!-- 配电自动化 -->
<div class="program-title" @click="clickStep">
{{ steps[3] }}
</div>
<div style="height: 300px"></div>
<!-- 智能电表及低压集抄 -->
<div class="program-title" @click="clickStep">
{{ steps[4] }}
</div>
<div style="height: 300px"></div>
<!-- 配电网通信 -->
<div class="program-title" @click="clickStep">
{{ steps[5] }}
</div>
<div style="height: 300px"></div>
<!-- 智能配电 -->
<div class="program-title" @click="clickStep">
{{ steps[6] }}
</div>
<div style="height: 300px"></div>
</div>
steps = [
'基础信息',
'中压部分',
'低压部分',
'自动化',
'电表',
'通信',
'配电',
]
activeStep = 1
/**
* 点击步骤
*/
clickStep(e) {
const title = e.target.innerText
const index = this.steps.findIndex(v => v === title)
this.activeStep = index + 1
// 获取对应的标题元素
const titleElement = document.querySelectorAll('.program-title')[index];
// 滚动到标题元素所在的位置
titleElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
}