修复冲突

dev
linyongLynn 2 weeks ago
commit 65191b137c

@ -3,8 +3,12 @@ ENV = 'production'
# base api
VUE_APP_BASE_API=https://wx.sstbc.com/
VUE_APP_UPLOAD_API=https://wx.sstbc.com/api/admin/upload-file
# VUE_APP_BASE_API=https://wx.sstbc.com/
# VUE_APP_UPLOAD_API=https://wx.sstbc.com/api/admin/upload-file
VUE_APP_BASE_API=''
VUE_APP_UPLOAD_API='/api/admin/upload-file'
VUE_APP_PRO_API =https://www.sstbc.com

@ -0,0 +1,55 @@
import request from "@/utils/request";
function customParamsSerializer(params) {
let result = '';
for (let key in params) {
if (params.hasOwnProperty(key)) {
if (Array.isArray(params[key])) {
params[key].forEach((item, index) => {
if (item.key) {
result += `${key}[${index}][key]=${item.key}&${key}[${index}][op]=${item.op}&${key}[${index}][value]=${item.value}&`;
} else {
result += `${key}[${index}]=${item}&`
}
});
} else {
result += `${key}=${params[key]}&`;
}
}
}
return result.slice(0, -1);
}
export function index(params, isLoading = false) {
return request({
method: "get",
url: "/api/admin/course-content-check/index",
params,
paramsSerializer: customParamsSerializer,
isLoading
})
}
export function show(params, isLoading = true) {
return request({
method: "get",
url: "/api/admin/course-content-check/show",
params,
isLoading
})
}
export function save(data) {
return request({
method: "post",
url: "/api/admin/course-content-check/save",
data
})
}
export function destroy(params) {
return request({
method: "get",
url: "/api/admin/course-content-check/destroy",
params
})
}

@ -197,4 +197,4 @@ export default {
methods: {
},
};
};

@ -78,7 +78,7 @@
</div>
<div class="xy-table-item-content">
<el-select filterable v-model="form.direction" style="width:100%" placeholder="请选择课程方向" clearable>
<el-option v-for="item in direction_options" :key="item.id" :label="item.value" :value="item.id">
<el-option v-for="item in direction_options" :key="item.id" :label="item.value" :value="item.value">
</el-option>
</el-select>
</div>

@ -0,0 +1,392 @@
<template>
<el-dialog
title="签到记录"
:visible.sync="isShow"
width="80%"
:before-close="handleClose"
class="sign-list-dialog"
>
<!-- 课程信息 -->
<div v-if="courseInfo" class="course-info-section">
<h3><i class="el-icon-document" /> 课程信息</h3>
<div class="info-grid">
<div class="info-item">
<span class="label">课程名称</span>
<span class="value">{{ courseInfo.name }}</span>
</div>
<div v-if="courseInfo.start_date" class="info-item">
<span class="label">开始时间</span>
<span class="value">{{ courseInfo.start_date }}</span>
</div>
<div v-if="courseInfo.end_date" class="info-item">
<span class="label">结束时间</span>
<span class="value">{{ courseInfo.end_date }}</span>
</div>
</div>
</div>
<!-- 课表信息 -->
<div v-if="scheduleInfo" class="schedule-info-section">
<h3><i class="el-icon-date" /> 课表信息</h3>
<div class="info-grid">
<div class="info-item">
<span class="label">主题</span>
<span class="value">{{ scheduleInfo.theme }}</span>
</div>
<div class="info-item">
<span class="label">时间</span>
<span class="value">{{ scheduleInfo.date }} - {{ scheduleInfo.period }}</span>
</div>
<div class="info-item">
<span class="label">地点</span>
<span class="value">{{ scheduleInfo.address }}</span>
</div>
<div v-if="scheduleInfo.teacher" class="info-item">
<span class="label">主讲</span>
<span class="value">{{ scheduleInfo.teacher.name }}</span>
</div>
</div>
</div>
<!-- 查询组 -->
<div class="search-section">
<h3><i class="el-icon-search" /> 查询条件</h3>
<el-form :model="searchForm" :inline="true" class="search-form">
<el-form-item label="姓名:">
<el-input
v-model="searchForm.name"
placeholder="请输入姓名"
clearable
style="width: 200px"
/>
</el-form-item>
<el-form-item label="联系方式:">
<el-input
v-model="searchForm.mobile"
placeholder="请输入联系方式"
clearable
style="width: 200px"
/>
</el-form-item>
<el-form-item label="签到状态:">
<el-select
v-model="searchForm.has_check"
placeholder="请选择签到状态"
clearable
style="width: 150px"
>
<el-option label="已签到" :value="1" />
<el-option label="未签到" :value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
查询
</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">
重置
</el-button>
<el-button type="success" icon="el-icon-download" @click="handleExport">
导出
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 数据表格 -->
<div class="table-section">
<h3><i class="el-icon-s-grid" /> 签到记录
<span style="color: #999;font-size: 16px;margin-left: 10px;">已签到<span style="color: #409EFF;">{{ course_content_check_count }}</span>未签到<span style="color: red;">{{ course_content_check_count_no }}</span></span>
</h3>
<xy-table
ref="signTable"
:height="400"
:list="list"
:req-opt="tableReqOpt"
:table-item="tableColumns"
:is-page="true"
:total="total"
/>
</div>
</el-dialog>
</template>
<script>
import { index as getSignList } from '@/api/sign/index.js'
import { download } from '@/utils/downloadRequest'
export default {
name: 'SignList',
props: {
courseId: {
type: [String, Number],
default: null
},
courseContentId: {
type: [String, Number],
default: null
},
courseInfo: {
type: Object,
default: null
},
scheduleInfo: {
type: Object,
default: null
}
},
data() {
return {
isShow: false,
course_content_check_count:0,
course_content_check_count_no:0,
searchForm: {
page: 1,
page_size: 10,
name: '',
mobile: '',
has_check: ''
},
tableReqOpt: {
course_id: this.courseId,
course_content_id: this.courseContentId || ''
},
list:[],
total:0,
tableColumns: [
{
prop: 'user.name',
label: '姓名',
align: 'center',
width: 120
},
{
prop: 'user.mobile',
label: '联系方式',
align: 'center',
width: 150
},
{
prop: 'course_content_check',
label: '签到状态',
align: 'center',
width: 120,
customFn: (scope) => {
const hasCheckRecord = scope.course_content_check
const statusText = hasCheckRecord ? '已签到' : '未签到'
const statusType = hasCheckRecord ? 'success' : 'danger'
return (
<el-tag type={statusType} size='small'>
{statusText}
</el-tag>
)
}
},
{
prop: 'course_content_check.created_at',
label: '签到时间',
align: 'center'
// width: 180
}
]
}
},
watch: {
courseId(newVal) {
this.tableReqOpt.course_id = newVal
},
courseContentId(newVal) {
this.tableReqOpt.course_content_id = newVal || ''
}
},
methods: {
//
show() {
this.isShow = true
this.$nextTick(() => {
this.refreshTable()
})
},
//
handleClose() {
this.isShow = false
this.resetForm()
},
//
handleSearch() {
this.tableReqOpt = {
...this.tableReqOpt,
...this.searchForm
}
this.refreshTable()
},
//
handleReset() {
this.resetForm()
this.tableReqOpt = {
course_id: this.courseId,
course_content_id: this.courseContentId || ''
}
this.refreshTable()
},
//
handleExport() {
if (!this.courseId && !this.courseContentId) {
this.$message.warning('缺少课程或课表信息,无法导出')
return
}
//
const exportFields = {
'user.name': '姓名',
'user.mobile': '联系方式',
'course_content_check_text': '签到状态',
'course_content_check_created_at': '签到时间'
}
//
const exportParams = {
...this.searchForm,
course_id: this.courseId,
course_content_id: this.courseContentId || '',
export_fields: exportFields,
is_export: 1,
clear: 1,
page: 1,
page_size: 999
}
//
let fileName = '签到记录'
if (this.courseInfo && this.courseInfo.name) {
fileName = `${this.courseInfo.name}-签到记录`
} else if (this.scheduleInfo && this.scheduleInfo.theme) {
fileName = `${this.scheduleInfo.theme}-签到记录`
}
// API
download(
'/api/admin/course-content-check/index',
'get',
exportParams,
`${fileName}.xlsx`
)
},
//
resetForm() {
this.searchForm = {
page: 1,
page_size: 10,
name: '',
mobile: '',
has_check: ''
}
},
//
refreshTable() {
this.fetchSignList()
},
//
async fetchSignList(params) {
const res = await getSignList({
...this.searchForm,
course_id: this.courseId,
course_content_id: this.courseContentId || '',
})
this.list = res.list.data
this.course_content_check_count = res.course_content_check_count
this.course_content_check_count_no = res.list.total - res.course_content_check_count
this.total = res.list.total
}
}
}
</script>
<style lang="scss" scoped>
.sign-list-dialog {
.course-info-section,
.schedule-info-section,
.search-section,
.table-section {
margin-bottom: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border: 1px solid #e9ecef;
h3 {
margin: 0 0 15px 0;
font-size: 16px;
font-weight: 600;
color: #2c3e50;
display: flex;
align-items: center;
gap: 8px;
i {
color: #3498db;
}
}
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 12px;
.info-item {
display: flex;
align-items: center;
.label {
font-weight: 500;
color: #6c757d;
margin-right: 8px;
min-width: 80px;
}
.value {
color: #2c3e50;
font-weight: 500;
}
}
}
.search-form {
.el-form-item {
margin-bottom: 15px;
}
}
.table-section {
h3 {
margin-bottom: 15px;
}
}
}
::v-deep .el-dialog__body {
padding: 20px;
max-height: 70vh;
overflow-y: auto;
}
::v-deep .el-dialog__header {
background: #f8f9fa;
border-bottom: 1px solid #e9ecef;
padding: 15px 20px;
.el-dialog__title {
font-size: 18px;
font-weight: 600;
color: #2c3e50;
}
}
</style>

@ -95,7 +95,7 @@
<div v-else>
<div class="qr-container">
<h4 style="margin-bottom: 20px; color: #2c3e50">
{{ is_arrange === 0 ? selectedCourse.name : selectedCourse.course.name }}
{{ is_arrange === 0 ? (selectedCourse && selectedCourse.name) : (selectedCourse && selectedCourse.course && selectedCourse.course.name) }}
</h4>
<div class="qr-code" v-if="qrImgUrl">
<img
@ -108,32 +108,32 @@
<h3><i class="el-icon-info"></i> 课表信息</h3>
<div class="qr-info-grid">
<div v-if="is_arrange === 0">
<strong>课程名称</strong>{{ selectedCourse.name }}
<strong>课程名称</strong>{{ selectedCourse && selectedCourse.name }}
</div>
<div v-if="is_arrange === 0">
<strong>开始时间</strong>{{ selectedCourse.start_date }}
<strong>开始时间</strong>{{ selectedCourse && selectedCourse.start_date }}
</div>
<div v-if="is_arrange === 0">
<strong>结束时间</strong>{{ selectedCourse.end_date }}
<strong>结束时间</strong>{{ selectedCourse && selectedCourse.end_date }}
</div>
<div v-if="is_arrange === 1">
<strong>主题</strong>{{ selectedCourse.theme }}
<strong>主题</strong>{{ selectedCourse && selectedCourse.theme }}
</div>
<div v-if="is_arrange === 1">
<strong>时间</strong>{{ selectedCourse.date }} -
{{ selectedCourse.period }}
<strong>时间</strong>{{ selectedCourse && selectedCourse.date }} -
{{ selectedCourse && selectedCourse.period }}
</div>
<div v-if="is_arrange === 1">
<strong>地点</strong>{{ selectedCourse.address }}
<strong>地点</strong>{{ selectedCourse && selectedCourse.address }}
</div>
<div v-if="is_arrange === 1">
<strong>主讲</strong
>{{
selectedCourse.teacher ? selectedCourse.teacher.name : ""
selectedCourse && selectedCourse.teacher ? selectedCourse.teacher.name : ""
}}
</div>
<div v-if="is_arrange === 0">
<strong>签到地点</strong>{{ selectedCourse.address_detail || '未设置' }}
<strong>签到地点</strong>{{ selectedCourse && (selectedCourse.address_detail || '未设置') }}
</div>
</div>
</div>
@ -145,6 +145,13 @@
@click="downloadQR"
>下载二维码</el-button
>
<el-button
type="success"
icon="el-icon-view"
class="btn-qr"
@click="showSignList"
>查看签到记录</el-button
>
<!-- <el-button type="success" icon="el-icon-view" class="btn-qr" @click="previewCheckin"></el-button> -->
<!-- <el-button type="info" icon="el-icon-document-copy" class="btn-qr" @click="copyUrl"></el-button> -->
</div>
@ -164,6 +171,13 @@
</div>
<editClass ref="editClass" @refresh="getCousreContent"></editClass>
<addCourse ref="addCourse" @refresh="onAddCourseRefresh"></addCourse>
<signList
ref="signList"
:course-id="course_id"
:course-content-id="selectedCourse ? selectedCourse.id : null"
:course-info="is_arrange === 0 ? selectedCourse : null"
:schedule-info="is_arrange === 1 ? selectedCourse : null"
/>
</div>
</template>
@ -175,12 +189,14 @@ import {
} from "@/api/course/courseContent.js";
import editClass from "@/views/course/components/editClass.vue";
import addCourse from "@/views/course/components/addCourse.vue";
import signList from "@/views/courseQr/components/signList.vue";
import { index as teacherIndex } from "@/api/info/teachers.js";
export default {
name: "CourseQr",
components: {
editClass,
addCourse,
signList,
},
data() {
return {
@ -272,6 +288,8 @@ export default {
this.course_id = "";
this.course_date = "";
this.courseContentList = [];
this.selectedCourse = null;
this.qrImgUrl = "";
this.getCourse(e);
},
async getCourse() {
@ -281,13 +299,17 @@ export default {
filter: [
{
key: "course_status",
op: "eq",
value: 10,
op: "in",
value: '10,20,30',
},
{
key: "is_arrange",
op: "eq",
value: this.is_arrange ? this.is_arrange : 0,
},{
key: "is_virtual",
op: "eq",
value: 0,
},
],
});
@ -370,8 +392,8 @@ export default {
const link = document.createElement("a");
link.href = this.qrImgUrl;
const fileName = this.is_arrange === 0
? `${this.selectedCourse.name}-签到二维码.png`
: `${this.selectedCourse.theme}-签到二维码.png`;
? `${this.selectedCourse && this.selectedCourse.name}-签到二维码.png`
: `${this.selectedCourse && this.selectedCourse.theme}-签到二维码.png`;
link.download = fileName;
link.target = "_blank";
link.click();
@ -418,6 +440,15 @@ export default {
}
});
},
//
showSignList() {
if (!this.selectedCourse) {
this.$message.warning('请先选择课程或课表');
return;
}
this.$refs.signList.show();
},
},
computed: {},
};

@ -660,4 +660,4 @@
}
}
</style>
</style>

@ -28,6 +28,7 @@ module.exports = {
*/
publicPath: process.env.ENV === 'staging' ? '/admin' : '/admin',
outputDir: '/Users/mac/Documents/朗业/2025/s-苏州科技商学院/wx.sstbc.com/public/admin',
// outputDir: '/Users/mac/Documents/朗业/2024/s-苏州科技商学院/wx.sstbc.com/public/admin',
assetsDir: 'static',
css: {
loaderOptions: { // 向 CSS 相关的 loader 传递选项
@ -63,7 +64,7 @@ module.exports = {
pathRewrite: {
['^' + process.env.VUE_APP_PRO_API]: process.env.VUE_APP_PRO_API
}
},
}
}
},
configureWebpack: {

Loading…
Cancel
Save