更新表格内弹出框、日期选择功能 #1

Merged
zhangqi merged 1 commits from zq-popup into master 1 month ago
  1. 7405
      package-lock.json
  2. 1
      package.json
  3. 32
      src/App.vue
  4. 27
      src/components/CustomDropdown.vue
  5. 293
      src/components/super/DateSelect.vue
  6. 4
      src/main.js
  7. 200
      src/views/super/Ranking/YearProfit.vue

7405
package-lock.json

File diff suppressed because it is too large

1
package.json

@ -13,6 +13,7 @@
"element-ui": "^2.15.14",
"regenerator-runtime": "^0.14.1",
"vue": "^2.6.14",
"vue-clickaway": "^2.2.2",
"vue-router": "^3.5.1",
"vuex": "^3.6.2"
},

32
src/App.vue

@ -8,15 +8,15 @@
<!-- <router-view/> -->
<el-container>
<el-header style="height: 62px;" v-if="showHeader">
<Header ></Header>
<Header></Header>
</el-header>
<el-container :class="(showHeader ? 'short-container': '')">
<el-container :class="(showHeader ? 'short-container' : '')">
<SliderMenu v-if="showSidebar"></SliderMenu>
<el-container>
<!-- 面包屑导航 -->
<Breadcrumb />
<el-main >
<router-view/>
<el-main>
<router-view />
</el-main>
<!-- <el-footer v-if="showFooter">
<Footer></Footer>
@ -66,9 +66,19 @@ export default {
Breadcrumb
},
computed: {
...mapState(['showSidebar','showFooter','showHeader']) // VuexshowSidebar
...mapState(['showSidebar', 'showFooter', 'showHeader']) // VuexshowSidebar
},
mounted() {
window.addEventListener('beforeunload', this.clearStorage);
},
beforeUnmount() {
window.removeEventListener('beforeunload', this.clearStorage);
},
methods: {
clearStorage() {
//
localStorage.removeItem('date');
},
handleOpen(key, keyPath) {
console.log(key, keyPath);
},
@ -79,7 +89,6 @@ export default {
}
</script>
<style lang="scss" scoped>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
font-family: Microsoft YaHei UI;
@ -90,14 +99,17 @@ export default {
height: 100%;
background: #F5F7FA;
}
.short-container{
.short-container {
height: calc(100vh - 62px);
}
::v-deep .el-container .el-main{
::v-deep .el-container .el-main {
// padding: 12px;
}
::v-deep .el-header{
height:62px;
::v-deep .el-header {
height: 62px;
padding: 8px 32px;
background: linear-gradient(270deg, #4EA4F3 0%, #3D85EA 100%);
}

27
src/components/CustomDropdown.vue

@ -1,5 +1,5 @@
<template>
<div class="custom-select" :class="{ 'is-open': isOpen }">
<div class="custom-select" v-clickaway="closeDropdown" ref="dropdown" :class="{ 'is-open': isOpen }" :style="{width}">
<!-- 触发按钮 -->
<div class="select-trigger" @click="toggleDropdown">
<slot name="trigger">
@ -13,12 +13,15 @@
<transition name="slide-fade">
<div v-if="isOpen" class="select-dropdown">
<slot v-if="isOpen" name="normal"></slot>
<div v-if="options">
<div v-for="(item, index) in options" :key="index" class="dropdown-item"
:class="{ 'is-selected': isSelected(item) }" @click="selectItem(item)">
<slot name="item" :item="item">
{{ item.label }}
</slot>
</div>
</div>
<!-- <slot v-if="isOpen" name="options_null"></slot> -->
<div class="flex-between dropdown-item" v-if="options_null" @click="selectNullItem">
<div class="left">
@ -35,11 +38,16 @@
</template>
<script>
// import { mixin as clickaway } from 'vue-clickaway';
export default {
props: {
width:{
type: String,
default: "200px",
},
options: {
type: Array,
required: true,
// required: true,
default: () => [],
},
options_null: {
@ -56,12 +64,20 @@ export default {
},
},
// mixins: [clickaway],
data() {
return {
isOpen: false,
selectedItem: null,
};
},
mounted(){
// document.addEventListener('click', this.handleClickOutside);
},
beforeUnmount() {
//
// document.removeEventListener('click', this.handleClickOutside);
},
watch: {
value: {
immediate: true,
@ -71,6 +87,9 @@ export default {
},
},
methods: {
closeDropdown() {
this.isOpen = false;
},
toggleDropdown() {
this.isOpen = !this.isOpen;
},
@ -129,8 +148,8 @@ export default {
.select-dropdown {
position: absolute;
top: 100%;
left: 0;
width: calc(100% - 24px);
right: 0;
width: auto;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #fff;

293
src/components/super/DateSelect.vue

@ -0,0 +1,293 @@
<template>
<div class="date-picker">
<div class="header">
<div class="current-date year-selector" v-if="view === 'month'" >
<button>&lt;</button>
<span @click="getMonYear">{{ currentYear !== selectedYear ? selectedYear : currentYear }}</span>
<button>&gt;</button>
</div>
<div class="selectYear" v-if="view !== 'month'" >
<span class="year">{{view == 'monthTwo' ? '选择年份':'' }}</span>
</div>
<div class="controls">
<button @click="toggleView('month')" :class="{ active: view !== 'year' }">月度</button>
<button @click="toggleView('year')" :class="{ active: view === 'year' }">年度</button>
</div>
</div>
<div class="selector" v-if="view === 'month'">
<div class="month-grid">
<button
v-for="month in months"
:key="month.value"
@click="selectMonth(month.value)"
:class="{ active: month.value === currentMonth && currentYear === selectedYear }"
>
{{ month.label }}
</button>
</div>
</div>
<div class="selector" v-if="view !== 'month'">
<div :class="[view == 'monthTwo' ? 'monthTwo-grid' :'','year-grid']">
<button
v-for="year in visibleYears"
:key="year"
@click="selectYear(year)"
:class="{
yearActive: year === currentYear,
current: year === new Date().getFullYear(),
}"
>
{{ year }}
</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'DatePicker',
props: {
modelValue: {
type: Date,
default: () => new Date()
},
view:{
type:String,
default:'month'
}
},
data() {
return {
// view: 'month', // 'month' or 'year monthTwo'
selectedYear: this.modelValue.getFullYear(),
currentMonth: this.modelValue.getMonth() + 1,
currentYear: this.modelValue.getFullYear(),
decadeStart: Math.floor(this.modelValue.getFullYear() / 10) * 10,
months: [
{ value: 1, label: '1月' },
{ value: 2, label: '2月' },
{ value: 3, label: '3月' },
{ value: 4, label: '4月' },
{ value: 5, label: '5月' },
{ value: 6, label: '6月' },
{ value: 7, label: '7月' },
{ value: 8, label: '8月' },
{ value: 9, label: '9月' },
{ value: 10, label: '10月' },
{ value: 11, label: '11月' },
{ value: 12, label: '12月' }
]
};
},
computed: {
decadeEnd() {
return this.decadeStart + 9;
},
visibleYears() {
const years = [];
for (let i = 0; i < 12; i++) {
years.push(this.decadeStart + i);
}
return years;
}
},
mounted(){
let selectedDate = new Date()
const oldDate = JSON.parse(localStorage.getItem('date'))
if(oldDate){
selectedDate = new Date(oldDate)
this.currentMonth = selectedDate.getMonth() + 1;
this.currentYear = selectedDate.getFullYear();
this.selectedYear = this.currentYear;
this.decadeStart = Math.floor(this.currentYear / 10) * 10;
}else{
selectedDate = new Date()
}
},
beforeUnmount() {
//
// this.$emit('update-count', 'month')
},
destroyed(){
this.$emit('update-count', 'month')
console.log('destroyed====');
},
methods: {
getMonYear(){
this.$emit('update-count', 'monthTwo')
},
toggleView(viewType) {
this.$emit('update-count', viewType)
},
selectMonth(month) {
this.currentMonth = month;
this.currentYear = this.selectedYear;
this.$emit('update-count', 'month')
this.emitDate();
},
selectYear(year) {
this.selectedYear = year;
this.currentYear = this.selectedYear;
if(this.view == 'monthTwo'){
this.$emit('update-count', 'month')
}
this.emitDate();
},
prevYear() {
this.selectedYear--;
this.updateDecade();
},
nextYear() {
this.selectedYear++;
this.updateDecade();
},
prevDecade() {
this.decadeStart -= 10;
},
nextDecade() {
this.decadeStart += 10;
},
updateDecade() {
this.decadeStart = Math.floor(this.selectedYear / 10) * 10;
},
emitDate() {
const selectedDate = new Date(this.currentYear, this.currentMonth - 1, 1);
this.$emit('update:modelValue', selectedDate);
this.$emit('change', selectedDate);
}
},
watch: {
modelValue(newVal) {
let selectedDate = newVal
this.currentMonth = selectedDate.getMonth() + 1;
this.currentYear = selectedDate.getFullYear();
this.selectedYear = this.currentYear;
this.decadeStart = Math.floor(this.currentYear / 10) * 10;
}
}
};
</script>
<style scoped>
.date-picker {
font-family: Microsoft YaHei UI;
width: 300px;
/* border: 1px solid #ddd; */
border-radius: 4px;
padding: 10px 0 0 0;
/* box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); */
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
padding-bottom: 8px;
border-bottom: 1px solid #F1F2F5;
letter-spacing: 0.08em;
color: #626573;
}
.current-date {
font-weight: bold;
}
.selectYear{
font-size: 14px;
font-weight: normal;
line-height: normal;
text-align: center;
letter-spacing: 0.08em;
color: #006AFF;
}
.controls{
border-radius: 4px;
background: #EAECF0;
box-sizing: border-box;
border: 1px solid #EAECF0;
padding: 3px 6px;
}
.selector{
}
.controls button {
background: none;
border: none;
padding: 1px 12px;
cursor: pointer;
}
.controls button.active {
background-color: #FFFFFF;
color: #006AFF;
/* border-color: #1890ff; */
}
.year-selector, .decade-selector {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.year-selector button, .decade-selector button {
background: none;
border: none;
cursor: pointer;
font-size: 16px;
}
.year-selector{
margin-bottom: 0;
}
.month-grid, .year-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 5px;
}
.month-grid button, .year-grid button {
font-family: Microsoft YaHei UI;
padding: 8px;
border: none;
display: flex;
align-items: center;
justify-content: center;
width: 48px;
background: none;
cursor: pointer;
border-radius: 20px;
color: #1E2226;
box-sizing: border-box;
}
.month-grid button:hover, .year-grid button:hover {
background-color: #F2F7FF;
}
.month-grid button.active, .year-grid button.active,.year-grid button.yearActive{
background-color: #006AFF;
color: white !important;
}
.monthTwo-grid button.current {
/* background-color: #006AFF !important;
color: white !important;
border-color: #1890ff !important; */
color: #fff;
}
.year-grid button.current{
/* font-weight: bold; */
color: #006AFF;
/* background-color: #F2F7FF; */
/* border-color: #1890ff; */
}
</style>

4
src/main.js

@ -9,10 +9,14 @@ import './style/theme/index.css'
import './style/theme/common.scss'
import 'core-js/stable';
import 'regenerator-runtime/runtime';
// main.js
import { directive as clickaway } from 'vue-clickaway';
Vue.config.productionTip = false
Vue.prototype.$http = request;
Vue.prototype.reqUri = '//admin.pengda.checkcopy.com';
Vue.use(ElementUI);
Vue.directive('clickaway', clickaway);
new Vue({
router,
store,

200
src/views/super/Ranking/YearProfit.vue

@ -2,35 +2,45 @@
<div class="demo-wrap min-flex-right">
<div class="flex-between">
<h2>总利润 - 年排行</h2>
<GuipForm />
<CustomDropdown :placeholder="text" width="280px">
<DateSelect slot="normal" :view="view" v-model="selectedDate" @update-count="handleUpdateView" @change="handleDateChange" />
</CustomDropdown>
</div>
<div class=" flex-common" id="">
<el-form>
<el-table :data="tableData" style="width: 100%" @sort-change="handleSortChange" @cell-mouse-enter="handleRowHover">
<el-table-column type="index" label="排序" width="100">
<template #default="scope">
{{ scope.row.sort }}
</template>
<el-table :data="tableData" style="width: 100%" @sort-change="handleSortChange"
@cell-mouse-enter="handleRowHover">
<el-table-column prop="sort" label="排序" width="100">
</el-table-column>
<!-- 其他列 -->
<el-table-column prop="1" label="年份" sortable="custom">
<template #default="scope">
{{ scope.row.value_1 }}
</template>
<el-table-column prop="value_1" label="年份" sortable="custom">
</el-table-column>
<el-table-column prop="2" label="总利润/元" sortable="custom">
<template #default="scope">
<div class="flex">
{{ scope.row.value_2 }}
</div>
</template>
<el-table-column prop="value_2" label="总利润/元" sortable="custom">
</el-table-column>
<el-table-column label="代理商排行">
<template #default="scope">
<span v-if="profit_top_list[scope.row.id]['name']">
No.1 {{ profit_top_list[scope.row.id]['name'] }} <img v-if="show_detail_index == scope.row.sort" class="detail_icon" src="../../../assets/super/list-detail.svg" alt="">
<template v-slot="{ row, $index }">
<el-popover v-model="row.id_popover" placement="top" trigger="manual"
:ref="`popover-${$index}`" @show="popshow">
<div class="pop-wrap">
<div class="flex-between flex pop-top">
<h3>{{ row.id }} <span @click="goLookMoreData">查看更多</span></h3>
<span class="flex point" @click="closePop(row,'id')">关闭 <img src="@/assets/register/close.svg" alt=""></span>
</div>
<el-table :data="tableData1" style="width: 100%" @sort-change="handleSortChange"
@cell-mouse-enter="handleRowHover">
<el-table-column prop="value_1" width="208" label="日期/月"></el-table-column>
<el-table-column prop="value_2" width="348" label="毛利润/元"
sortable="custom"></el-table-column>
</el-table>
</div>
<!-- 触发弹框的按钮 -->
<span v-if="profit_top_list[row.id]['name']" slot="reference"
@click="handleClick(row, $index, 'id')">
No.1 {{ profit_top_list[row.id]['name'] }} <img v-if="show_detail_index == row.sort"
class="detail_icon" src="@/assets/super/list-detail.svg" alt="">
</span>
</el-popover>
</template>
</el-table-column>
</el-table>
@ -43,19 +53,31 @@
</div>
</template>
<script>
import GuipForm from '@/components/GuipForm.vue'
// import GuipForm from '@/components/GuipForm.vue'
import DateSelect from '@/components/super/DateSelect.vue';
import CustomDropdown from '@/components/CustomDropdown.vue';
export default {
//
name: '',
props: [''],
components: {
GuipForm
DateSelect,
CustomDropdown
},
data() {
return {
viewDesc:{
'month':'月份',
'monthTwo':'月份',
'year':'年份',
},
view:'month',
currentPage: 1, //
pageSize: 20, //
total: 0, //
text:'',//
selectedDate:new Date(),//
options_payword: [
{
label: '按篇', value: '0'
@ -71,49 +93,110 @@ export default {
// ...
],
filteredOptions: this.options,
tableData: [],
profit_top_list: [],
show_detail_index:0,
tableData: [
],
tableData1: [],
profit_top_list: {
},
show_detail_index: 0,
form: {
payword: '0',
}
}
},
mounted() {
// this.$refs.scrollContainer.scrollTo(0)
this.getRankingData();
this.text = this.getNowDate()//
this.getRankingData()
},
computed: {
},
methods: {
handleUpdateView(newView) {
this.view = newView;
},
getNowDate(){
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // 0
const currentYearMonth = `(${this.viewDesc[this.view]})${year}${month}`;
return `${currentYearMonth}`
},
getDate(dateStr){
const date = new Date(dateStr);
const year = date.getFullYear(); // 2025
const month = date.getMonth() + 1; // 3 (3)
if(this.view == 'year'){
return `(${this.viewDesc[this.view]})${year}`
}else{
return `(${this.viewDesc[this.view]})${year}${month}`
}
},
handleDateChange(date) {
this.text = this.getDate(date)
this.selectedDate = date;
localStorage.setItem('date',JSON.stringify(date))
//
// this.getRankingData()
console.log('日期已更改:', date);
},
goLookMoreData() {
this.$router.push('/')
},
closePop(row,type){
row[type + '_popover'] = false; //
},
handleClick(row, index, type) {
//
this.tableData.forEach((item, i) => {
if (i !== index) {
item[type + '_popover'] = false;
}
});
//
row[type + '_popover'] = true;
console.log(this.tableData,'this.tableData');
},
popshow() {
var ariaEls = document.querySelectorAll('.el-popover')
ariaEls.forEach((item) => {
item.removeAttribute('aria-hidden')
})
ariaEls = document.querySelectorAll('.el-radio__original')
ariaEls.forEach((item) => {
item.removeAttribute('aria-hidden')
})
},
handleSortChange({ prop, order }) {
this.currentPage = 1;
let sortBy = 0;
let sortOrder = 0;
if(order == 'ascending'){
if (order == 'ascending') {
sortBy = prop;
sortOrder = 1;
}
if(order == 'descending'){
if (order == 'descending') {
sortBy = prop;
sortOrder = 2;
}
this.getRankingData({sortBy:sortBy,sortOrder:sortOrder})
this.getRankingData({ sortBy: sortBy, sortOrder: sortOrder })
},
handleRowHover(row) {
this.show_detail_index = row.sort
},
getRankingData(obj){
getRankingData(obj) {
const that = this
this.$http('POST', this.reqUri + '/super/ajax_get_rank_detail',{
rank_type:1,
cur_page:that.currentPage,
page_size:that.pageSize,
this.$http('POST', this.reqUri + '/super/ajax_get_rank_detail', {
rank_type: 1,
cur_page: that.currentPage,
page_size: that.pageSize,
...obj
}).then(response => {
this.$nextTick(() => {
that.tableData = response.data.list
that.profit_top_list = response.data.profit_top_list
that.profit_top_list = response.data.top_list
that.total = response.data.total
})
})
@ -152,7 +235,50 @@ export default {
width: 100%;
letter-spacing: 0.08em;
}
.detail_icon{
.detail_icon {
vertical-align: text-top;
}
::v-deep .el-popover.el-popper {
padding: 20px;
}
.pop-wrap {
// width: 596px;
// height: 320px;
/* 自动布局 */
display: flex;
flex-direction: column;
// padding: 20px;
gap: 20px;
box-sizing: border-box;
// background: #FFFFFF;
/* 阴影/常规阴影 */
// box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.16);
.pop-top {
h3 {
/* body/body 1_bold */
font-family: Microsoft YaHei UI;
font-size: 16px;
font-weight: bold;
line-height: 18px;
letter-spacing: 0.08em;
color: #1D2129;
margin: 0;
span {
display: inline-block;
margin-left: 12px;
font-size: 14px;
line-height: 18px;
color: #006AFF;
}
}
img{
width: 20px;
height: 20px;
}
}
}
</style>
Loading…
Cancel
Save