集成任务日志明细本地化修改

This commit is contained in:
hyt 2024-05-16 13:35:30 +08:00
parent 74cea6da6a
commit 9c6e772276
2 changed files with 456 additions and 452 deletions

View File

@ -1,371 +1,369 @@
<template> <template>
<div> <div>
<base-layout ref="baseLayout" :buttonList="buttonList" @onFuncBtn="onFuncBtn" :querySwitch="true" <base-layout ref="baseLayout" :buttonList="buttonList" @onFuncBtn="onFuncBtn" :querySwitch="true"
:searchBtnList="searchBtnList" :searchList="requirementList" @search="handleSearchEvent" :isPage="true" :searchBtnList="searchBtnList" :searchList="requirementList" @search="handleSearchEvent" :isPage="true"
@pageChange="handlePageChange" @onFuncSearchBtn="onFuncSearchBtn"> @pageChange="handlePageChange" @onFuncSearchBtn="onFuncSearchBtn"
<div slot="main" slot-scope="{ tableHeight }"> >
<base-table ref="baseTable" :showIndex="true" :funWidth="350" :funData="funData" @onFunc="onFunc" <div slot="main" slot-scope="{ tableHeight }">
:tabLoading.sync="tabLoading" :tableHeight="tableHeight" :tableData="tableData" <base-table ref="baseTable" :showIndex="true" :funWidth="350" :funData="funData" @onFunc="onFunc"
:fixedTable="'right'" :tableColumn="tableColumnData" :showSelect="true"> :tabLoading.sync="tabLoading" :tableHeight="tableHeight" :tableData="tableData"
<template v-slot:new_state="{ row }"> :fixedTable="'right'" :tableColumn="tableColumnData" :showSelect="true"
<div style="width: 100%"> >
<span v-if="row.new_state == 'Y'" style="color: #67c23a">成功</span> <template v-slot:new_state="{ row }">
<span v-else-if="row.new_state == 'H'" style="color: #67c23a">已处理</span> <div style="width: 100%">
<span v-else style="color: #f56c6c">失败</span> <span v-if="row.new_state == 'Y'" style="color: #67c23a">成功</span>
</div> <span v-else-if="row.new_state == 'H'" style="color: #67c23a">已处理</span>
</template> <span v-else style="color: #f56c6c">失败</span>
</base-table> </div>
</div> </template>
</base-layout> </base-table>
<right-dialog ref="rightDialog" @resetTable="resetTable"></right-dialog>
</div> </div>
</base-layout>
<right-dialog ref="rightDialog" @resetTable="resetTable"></right-dialog>
</div>
</template> </template>
<script> <script>
import baseLayout from "@/components/base/baseLayout"; import baseLayout from '@/components/base/baseLayout'
import baseTable from "@/components/base/baseTable"; import baseTable from '@/components/base/baseTable'
import rightDialog from "./rightDialog"; import rightDialog from './rightDialog'
import configData from "./configData"; import configData from './configData'
import { import {
authApi authApi
} from "@/api/apis/auth"; } from '@/api/apis/auth'
import { option } from "@/api/apis/detailData" import { option } from '@/api/apis/detailData'
export default
{ export default {
components: { components: {
baseLayout, baseLayout,
baseTable, baseTable,
rightDialog, rightDialog
}, },
data() { data() {
return { return {
buttonList: [ buttonList: [
{ {
menuName: "刷新", menuName: '刷新',
icon: "el-icon-refresh", icon: 'el-icon-refresh',
btnFunction: "resetLoad", btnFunction: 'resetLoad'
}, }
], // ], //
searchBtnList: [ searchBtnList: [
{ {
name: "批量推送", name: '批量推送',
btnFunction: "batchPush", btnFunction: 'batchPush'
}, }
], // ], //
requirementList: [ requirementList: [
{ {
placeholder: "任务名称", placeholder: '任务名称',
prop: "plugin_id", prop: 'plugin_id',
tag: "elSelect", tag: 'elSelect',
options: [], options: [],
optionValue: "pluginId", optionValue: 'pluginId',
optionLabel: "pluginName", optionLabel: 'pluginName'
}, },
{ {
placeholder: "源系统编码", placeholder: '源系统编码',
prop: "root_app_bill", prop: 'root_app_bill',
tag: "elInput", tag: 'elInput'
}, },
{ {
placeholder: "推送时间", placeholder: '推送时间',
prop: "new_push_date", prop: 'new_push_date',
tag: "elDatePicker", tag: 'elDatePicker',
valueFormat: "yyyy-MM-dd", valueFormat: 'yyyy-MM-dd'
}, },
{ {
placeholder: "业务⽇期", placeholder: '业务⽇期',
prop: "business_date", prop: 'business_date',
tag: "elDatePicker", tag: 'elDatePicker',
valueFormat: "yyyy-MM-dd", valueFormat: 'yyyy-MM-dd'
}, },
{ {
placeholder: "下游系统单号", placeholder: '下游系统单号',
prop: "new_system_number", prop: 'new_system_number',
tag: "elInput", tag: 'elInput'
}, },
{ {
placeholder: "推送状态", placeholder: '推送状态',
prop: "new_state", prop: 'new_state',
tag: "elRadio", tag: 'elRadio',
options: [ options: [
{ label: "全部", value: "" }, { label: '成功', value: 'Y' },
{ label: "成功", value: "Y" }, { label: '已处理', value: 'H' },
{ label: "已处理", value: "H" }, { label: '失败', value: 'N' }
{ label: "失败", value: "N" }, ]
], }
}, ], //list
], //list tabLoading: false,
tabLoading: false, tableColumnData: configData.tableColumnData, //
tableColumnData: configData.tableColumnData, // funData: [
funData: [ {
{ color: '#6a9af1',
color: "#6a9af1", text: '查看'
text: "查看", },
}, {
{ color: '#6a9af1',
color: "#6a9af1", text: '重新推送'
text: "重新推送", },
}, {
{ color: '#6a9af1',
color: "#6a9af1", text: '线下处理'
text: "手工处理", },
}, {
{ color: '#d67a74',
color: "#d67a74", text: '删除'
text: "删除", }
}, ],
], tableData: [], //
tableData: [], // pageModel: {
pageModel: { pageNum: 1,
pageNum: 1, pageSize: 10
pageSize: 10, },
}, queryModel: {
queryModel: { plugin_id: '',
plugin_id: '', root_app_bill: '',
root_app_bill: '', new_push_date: '',
new_push_date: '', business_date: '',
business_date: '', new_system_number: '',
new_system_number: '', new_state: 'N'
new_state: 'N' }
}, }
}; },
}, mounted() {
mounted() { this.$refs.baseLayout.getField('new_state', 'N')
this.$refs.baseLayout.getField("new_state", "N"); this.queryProductClassfy()
this.queryProductClassfy(); this.GetProductionTableData()
this.GetProductionTableData(); },
}, methods: {
methods: { //
// async GetProductionTableData() {
async GetProductionTableData() { this.tabLoading = true
this.tabLoading = true; const res = await option({
const res = await option({ tl: 'integrationTaskLivingDetailsService',
tl: "integrationTaskLivingDetailsService", as: 'integrationTaskLog',
as: "integrationTaskLog", dj: 'queryPage'
dj: "queryPage", }, {
}, { ...this.pageModel,
...this.pageModel, ...this.queryModel
...this.queryModel })
}) this.tabLoading = false
this.tabLoading = false; if (res.status == '200') {
if (res.status == "200") { this.tableData = res.attribute.list
this.tableData = res.attribute.list; this.$refs.baseTable.setTableKey()
this.$refs.baseTable.setTableKey() this.$refs.baseLayout.setPageTotal(res.attribute.total)
this.$refs.baseLayout.setPageTotal(res.attribute.total); }
} },
}, async queryProductClassfy() {
async queryProductClassfy() { const res = await option({
const res = await option({ tl: 'pluginService',
tl: "pluginService", as: 'plugins',
as: "plugins", dj: 'queryPlugins'
dj: "queryPlugins", }, {})
}, {})
if (res.status == "200") { if (res.status == '200') {
this.requirementList[0].options = res.attribute; this.requirementList[0].options = res.attribute
} }
}, },
// //
handleSearchEvent() { handleSearchEvent() {
let data = this.$refs.baseLayout.ruleForm; let data = this.$refs.baseLayout.ruleForm
this.queryModel.plugin_id = data.plugin_id; this.queryModel.plugin_id = data.plugin_id
this.queryModel.root_app_bill = data.root_app_bill; this.queryModel.root_app_bill = data.root_app_bill
this.queryModel.new_push_date = data.new_push_date; this.queryModel.new_push_date = data.new_push_date
this.queryModel.business_date = data.business_date; this.queryModel.business_date = data.business_date
this.queryModel.new_system_number = data.new_system_number; this.queryModel.new_system_number = data.new_system_number
this.queryModel.new_state = data.new_state; this.queryModel.new_state = data.new_state
this.resetTable(); this.resetTable()
}, },
// //
async batchPush() { async batchPush() {
let data = '' let data = []
this.$refs.baseTable.$refs.elTable.selection.forEach(el => { this.$refs.baseTable.$refs.elTable.selection.forEach(el => {
if (data == '') { data.push(el)
data = el.id })
} else { if (data.length > 0) {
data += `,${el.id}` this.$confirm('确认重新推送吗?', '提示', {
} confirmButtonText: '确定',
}); cancelButtonText: '取消',
if (data.length > 0) { type: 'warning'
this.$confirm("确认重新推送吗?", "提示", { })
confirmButtonText: "确定", .then(() => {
cancelButtonText: "取消", this.openLoading('submit')
type: "warning", this.rePushBatch(data)
}) })
.then(() => { .catch(() => {
this.openLoading("submit"); this.$vmNews('取消操作', 'info')
this.rePushBatch(data) })
}) } else {
.catch(() => { this.$vmNews('请选择需要推送的单据', 'warning')
this.$vmNews("取消操作", "info"); }
}); },
} else { async rePushBatch(data) {
this.$vmNews("请选择需要推送的单据", "warning"); const res = await option({
} tl: 'integrationTaskLivingDetailsService',
}, as: 'integrationTaskLog',
async rePushBatch(data) { dj: 'batchPush'
const res = await option({ },
tl: "integrationTaskLivingDetailsService", data
as: "integrationTaskLog", )
dj: "batchPush", if (res.status == '200') {
}, { this.$vmNews('批量推送成功', 'success')
id: data this.resetTable()
}) }
if (res.status == "200") { },
this.$vmNews("批量推送成功", "success"); //
this.resetTable(); handlePageChange(val) {
} this.pageModel.pageNum = val.pageIndex
}, this.pageModel.pageSize = val.pageSize
// this.GetProductionTableData()
handlePageChange(val) { },
this.pageModel.pageNum = val.pageIndex; //
this.pageModel.pageSize = val.pageSize; onFuncBtn(btn) {
this.GetProductionTableData(); this[btn.btnFunction]()
}, },
// //
onFuncBtn(btn) { onFuncSearchBtn(btn) {
this[btn.btnFunction](); this[btn.btnFunction]()
}, },
// //
onFuncSearchBtn(btn) { add() {
this[btn.btnFunction](); this.$refs.rightDialog.openDialog('add')
}, },
// //
add() { async onFunc(index, row) {
this.$refs.rightDialog.openDialog("add"); //
}, if (index == 0) {
// this.openLoading('detail')
async onFunc(index, row) { this.$refs.rightDialog.openDialog('show', row)
// }
if (index == 0) { //
this.openLoading("detail"); if (index == 1) {
this.$refs.rightDialog.openDialog("show", row); this.$confirm('确认重新推送吗?', '提示', {
} confirmButtonText: '确定',
// cancelButtonText: '取消',
if (index == 1) { type: 'warning'
this.$confirm("确认重新推送吗?", "提示", { })
confirmButtonText: "确定", .then(() => {
cancelButtonText: "取消", this.openLoading('submit')
type: "warning", this.handleRePush(row)
}) })
.then(() => { .catch(() => {
this.openLoading("submit"); this.$vmNews('取消操作', 'info')
this.handleRePush(row); })
}) }
.catch(() => { //
this.$vmNews("取消操作", "info"); if (index == 2) {
}); this.$prompt('请填写旺店通或者NC的线下单据号方便以后排查', '提示', {
} confirmButtonText: '提交',
// cancelButtonText: '取消',
if (index == 2) { inputType: 'textarea',
this.$prompt("请填写旺店通或者NC的单据号方便以后排查", "提示", { inputValidator: (value) => {
confirmButtonText: "提交", if (!value) {
cancelButtonText: "取消", return '请输入处理内容'
inputType: "textarea", }
inputValidator: (value) => { }
if (!value) { })
return "请输入处理内容"; .then(({ value }) => {
} this.openLoading('submit')
}, let params =
}) {
.then(({ value }) => { id: row.id,
this.openLoading("submit"); processing_remarks: value
let params = }
{
id: row.id, this.updateManualProcess(params)
processing_remarks: value, })
} .catch(() => {
; this.$message({
this.updateManualProcess(params); type: 'info',
}) message: '取消操作'
.catch(() => { })
this.$message({ })
type: "info", }
message: "取消操作", //
}); if (index == 3) {
}); this.$confirm('确认删除该内容吗?', '提示', {
} confirmButtonText: '确定',
// cancelButtonText: '取消',
if (index == 3) { type: 'warning'
this.$confirm("确认删除该内容吗?", "提示", { })
confirmButtonText: "确定", .then(() => {
cancelButtonText: "取消", this.openLoading('del')
type: "warning", this.productionDeleteById(row)
}) })
.then(() => { .catch(() => {
this.openLoading("del"); this.$vmNews('取消操作', 'info')
this.productionDeleteById(row.id); })
}) }
.catch(() => { },
this.$vmNews("取消操作", "info"); async handleRePush(row) {
}); const res = await option({
} tl: 'integrationTaskLivingDetailsService',
}, as: 'integrationTaskLog',
async handleRePush(row) { dj: 'repush'
const res = await option({ }, {
tl: "integrationTaskLivingDetailsService", id: row.id,
as: "integrationTaskLog", new_state: row.new_state
dj: "repush", })
}, { if (res.status == '200') {
id: row.id this.$vmNews('补推成功', 'success')
}) this.resetTable()
if (res.status == "200") { } else if (res.status == '500') {
this.$vmNews("补推成功", "success"); this.$message({
this.resetTable(); title: '补推失败',
} else if (res.status == "500") { showClose: true,
this.$message({ message: res.msg,
title: "补推失败", duration: 0,
showClose: true, type: 'error'
message: res.msg, })
duration: 0, this.resetTable()
type: "error", }
}); },
this.resetTable(); async updateManualProcess(val) {
} const res = await option({
}, tl: 'integrationTaskLivingDetailsService',
async updateManualProcess(val) { as: 'integrationTaskLog',
const res = await option({ dj: 'manualProcessing'
tl: "integrationTaskLivingDetailsService", }, val)
as: "integrationTaskLog", if (res.status == '200') {
dj: "manualProcessing", this.$vmNews('线下处理成功', 'success')
}, val) this.resetTable()
if (res.status == "200") { }
this.$vmNews("手工处理成功", "success"); },
this.resetTable(); async productionDeleteById(id) {
} const res = await option({
}, tl: 'integrationTaskLivingDetailsService',
async productionDeleteById(id) { as: 'integrationTaskLog',
const res = await option({ dj: 'deleteEntity'
tl: "integrationTaskLivingDetailsService", }, {
as: "integrationTaskLog", id: row.id,
dj: "deleteEntity", new_state: row.new_state
}, { })
id if (res.status == '200') {
}) this.$vmNews('删除成功', 'success')
if (res.status == "200") { this.resetTable()
this.$vmNews("删除成功", "success"); }
this.resetTable(); },
} //
}, resetTable() {
// this.pageModel.pageNum = 1
resetTable() { this.$refs.baseLayout.pageClear()
this.pageModel.pageNum = 1; this.GetProductionTableData()
this.$refs.baseLayout.pageClear(); }
this.GetProductionTableData(); }
}, }
},
};
</script> </script>
<style scoped> <style scoped>
.clickTitle { .clickTitle {
color: #409eff; color: #409eff;
cursor: pointer; cursor: pointer;
} }
::v-deep .searchBox:nth-child(6) { ::v-deep .searchBox:nth-child(6) {
width: 250px !important; width: 250px !important;
} }
</style> </style>

View File

@ -1,32 +1,36 @@
<template> <template>
<div> <div>
<base-right-dialog ref="baseRightDialog" :footerShow="true" :dialogVisible.sync="dialogVisible" <base-right-dialog ref="baseRightDialog" :footerShow="true" :dialogVisible.sync="dialogVisible"
:title="dialogTitle + ' 集成产品清单'" @handleClose="handleDialogClose" :type="dialogType" :submitShow="submitShow" :title="dialogTitle + ' 集成产品清单'" @handleClose="handleDialogClose" :type="dialogType"
@handleConfirmClick="handleConfirmClick"> :submitShow="submitShow"
@handleConfirmClick="handleConfirmClick"
>
<base-form ref="basicsForm" :formRow="formRow" :isFunBtn="false" :rules="basicsRules" class="dialog_form" <base-form ref="basicsForm" :formRow="formRow" :isFunBtn="false" :rules="basicsRules" class="dialog_form"
:spanWidth="`120px`" :loading="vLoading"></base-form> :spanWidth="`120px`" :loading="vLoading"
></base-form>
</base-right-dialog> </base-right-dialog>
</div> </div>
</template> </template>
<script> <script>
import baseRightDialog from "@/components/base/baseRightDialog"; import baseRightDialog from '@/components/base/baseRightDialog'
import baseForm from "@/components/base/baseNewForm"; import baseForm from '@/components/base/baseNewForm'
import baseTable from "@/components/base/baseTable"; import baseTable from '@/components/base/baseTable'
import configData from "./configData"; import configData from './configData'
import { authApi } from "@/api/apis/auth"; import { authApi } from '@/api/apis/auth'
import { option } from "@/api/apis/detailData" import { option } from '@/api/apis/detailData'
export default { export default {
components: { components: {
baseRightDialog, baseRightDialog,
baseForm, baseForm,
baseTable, baseTable
}, },
data() { data() {
return { return {
dialogVisible: false, dialogVisible: false,
dialogTitle: "", dialogTitle: '',
dialogType: "", dialogType: '',
formRow: configData.formRow, formRow: configData.formRow,
basicsRules: configData.basicsRules, basicsRules: configData.basicsRules,
vLoading: false, vLoading: false,
@ -37,55 +41,57 @@ export default {
tableVersionColumn: configData.tableVersionColumn, tableVersionColumn: configData.tableVersionColumn,
funData: [], funData: [],
isEdit: false isEdit: false
}; }
},
mounted() {
}, },
mounted() { },
methods: { methods: {
openDialog(type, row) { openDialog(type, row) {
// this.queryProductClassfy(); // this.queryProductClassfy();
this.formRow = configData.formRow; this.formRow = configData.formRow
this.submitShow = true; this.submitShow = true
this.isEdit = false this.isEdit = false
this.funData = [ this.funData = [
{ {
color: "#ff0000", color: '#ff0000',
text: "删除", text: '删除'
} }
] ]
// //
if (type == "add") { if (type == 'add') {
this.dialogTitle = "新增"; this.dialogTitle = '新增'
this.dialogType = "add"; this.dialogType = 'add'
} }
// //
if (type == "edit") { if (type == 'edit') {
this.isEdit = true this.isEdit = true
this.dialogTitle = "编辑"; this.dialogTitle = '编辑'
this.dialogType = "edit"; this.dialogType = 'edit'
this.productGetById(row.id); this.productGetById(row)
} }
// //
if (type == "show") { if (type == 'show') {
this.funData = Object.assign([], []) this.funData = Object.assign([], [])
this.submitShow = false; this.submitShow = false
this.formRow = configData.formRowShow; this.formRow = configData.formRowShow
this.dialogTitle = "查看"; this.dialogTitle = '查看'
this.dialogType = "show"; this.dialogType = 'show'
this.productGetById(row.id); this.productGetById(row)
} }
this.dialogVisible = true; this.dialogVisible = true
}, },
async productGetById(id) { async productGetById(row) {
this.openLoading("detail"); this.openLoading('detail')
const res = await option({ const res = await option({
tl: "integrationTaskLivingDetailsService", tl: 'integrationTaskLivingDetailsService',
as: "integrationTaskLog", as: 'integrationTaskLog',
dj: "queryEntity", dj: 'queryEntity'
}, { }, {
id id: row.id,
new_state: row.new_state
}) })
if (res.status == '200') { if (res.status == '200') {
this.$refs.basicsForm.incomingParameters(res.attribute); this.$refs.basicsForm.incomingParameters(res.attribute)
} }
}, },
@ -123,11 +129,11 @@ export default {
addVersionDialog() { addVersionDialog() {
let obj = { let obj = {
id: new Date().getTime().toString(), id: new Date().getTime().toString(),
versionNumber: "", versionNumber: '',
versionDescription: "", versionDescription: '',
disabled: false disabled: false
}; }
this.tableVersionData.push(obj); this.tableVersionData.push(obj)
}, },
// //
onFunc(index, row) { onFunc(index, row) {
@ -135,99 +141,99 @@ export default {
this.$delConfirm().then(() => { this.$delConfirm().then(() => {
this.tableVersionData.forEach((item, itemIndex) => { this.tableVersionData.forEach((item, itemIndex) => {
if (item.id === row.id) { if (item.id === row.id) {
this.tableVersionData.splice(itemIndex, 1); this.tableVersionData.splice(itemIndex, 1)
} }
}); })
}); })
} }
}, },
// //
handleDialogClose() { handleDialogClose() {
this.tableVersionData = Object.assign([], []); this.tableVersionData = Object.assign([], [])
this.$refs.basicsForm.resetFields(); this.$refs.basicsForm.resetFields()
this.dialogVisible = false; this.dialogVisible = false
}, },
// //
handleConfirmClick() { handleConfirmClick() {
this.$refs.basicsForm.$refs["ruleForm"].validate((valid) => { this.$refs.basicsForm.$refs['ruleForm'].validate((valid) => {
if (!valid) { if (!valid) {
return; return
} else { } else {
let params = { let params = {
...this.$refs.basicsForm.ruleForm, ...this.$refs.basicsForm.ruleForm,
sysProductVersionList: [], sysProductVersionList: []
}; }
let result = []; let result = []
if (this.tableVersionData.length > 0) { if (this.tableVersionData.length > 0) {
this.tableVersionData.forEach((item) => { this.tableVersionData.forEach((item) => {
let obj = { let obj = {
versionNumber: item.versionNumber, versionNumber: item.versionNumber,
versionDescription: item.versionDescription, versionDescription: item.versionDescription
}; }
result.push(obj); result.push(obj)
}); })
} }
params.sysProductVersionList = Object.assign([], result); params.sysProductVersionList = Object.assign([], result)
if (this.dialogType == "add") { if (this.dialogType == 'add') {
this.openLoading("submit"); this.openLoading('submit')
this.productSaveDto(params); this.productSaveDto(params)
} }
if (this.dialogType == "edit") { if (this.dialogType == 'edit') {
this.openLoading("submit"); this.openLoading('submit')
this.productUpdateDto(params); this.productUpdateDto(params)
} }
} }
}); })
}, },
// //
async productSaveDto(params) { async productSaveDto(params) {
let res = await authApi( let res = await authApi(
"sysProductService", 'sysProductService',
"product", 'product',
"addProduct", 'addProduct',
"", '',
params params
); )
if (res.status == "200") { if (res.status == '200') {
this.handleDialogClose(); this.handleDialogClose()
this.$vmNews("新增成功", "success"); this.$vmNews('新增成功', 'success')
this.$emit("resetTable"); this.$emit('resetTable')
} }
}, },
// //
async productUpdateDto(params) { async productUpdateDto(params) {
let res = await authApi( let res = await authApi(
"sysProductService", 'sysProductService',
"product", 'product',
"updateProduct", 'updateProduct',
"", '',
params params
); )
if (res.status == "200") { if (res.status == '200') {
this.handleDialogClose(); this.handleDialogClose()
this.$vmNews("更新成功", "success"); this.$vmNews('更新成功', 'success')
this.$emit("resetTable"); this.$emit('resetTable')
} }
}, },
// //
async queryProductClassfy() { async queryProductClassfy() {
let params = { let params = {
tab_name: "sys_product", tab_name: 'sys_product',
column_name: "classify", column_name: 'classify'
};
let res = await authApi(
"generalServiceImpl",
"dictionaryshop",
"selectDictionaryshop",
"",
params
);
if (res.status == "200") {
this.formRow[1].elCol[0].options = res.attribute;
} }
}, let res = await authApi(
}, 'generalServiceImpl',
}; 'dictionaryshop',
'selectDictionaryshop',
'',
params
)
if (res.status == '200') {
this.formRow[1].elCol[0].options = res.attribute
}
}
}
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">