This commit is contained in:
qsh
2026-02-03 11:46:59 +08:00
parent 20ab62b473
commit ec95ab4933
18 changed files with 197 additions and 967 deletions

View File

@@ -3,6 +3,8 @@ VITE_APP_TITLE = 寻驾
# 租户id
VITE_APP_TENANT_ID = 10001
# instance id
VITE_APP_INSTANCE_ID = 1038
VITE_APP_PHONE = 18056811878

View File

@@ -2,6 +2,8 @@
VITE_APP_TITLE = 寻驾
# 租户id
VITE_APP_TENANT_ID = 10001
# instance id
VITE_APP_INSTANCE_ID = 1038
VITE_APP_PHONE = 18056811878

View File

@@ -3,7 +3,9 @@
import { getToken } from '@/utils/auth'
import { useConfigStore } from '@/store'
import { useUserStore } from '@/store'
import { getCurrentInstance } from "vue"
import { useTenantStore } from '@/store'
import { onLaunch } from '@dcloudio/uni-app'
const { proxy } = getCurrentInstance()
@@ -24,6 +26,8 @@
function initConfig() {
useConfigStore().setConfig(config)
// 初始化租户信息
useTenantStore().getTenant({ id: config.tenantId })
}
function checkLogin() {

18
src/api/system/tenant.js Normal file
View File

@@ -0,0 +1,18 @@
import request from '@/utils/request';
export const getTenantInfo = params => {
return request({
url: '/applet/xunjia/tenant/get',
method: 'get',
params
});
};
// 修改租户
export const updateTenant = data => {
return request({
url: '/applet/xunjia/tenant/update',
method: 'put',
data
});
};

View File

@@ -7,6 +7,12 @@ export default {
name: '小程序管理后台',
// 应用版本
version: '1.0.0',
// 更新时间
updateTime: '2026-02-03',
// 租户id
tenantId: import.meta.env.VITE_APP_TENANT_ID,
// instance id
instanceId: import.meta.env.VITE_APP_INSTANCE_ID,
// 应用logo
logo: '/static/logo.png'
}

View File

@@ -112,6 +112,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { getVipType, addVipType, updateVipType } from '@/api/member'
import { useTenantStore } from '@/store'
// 表单数据
const formData = ref({
@@ -148,7 +149,7 @@
const unitIndex = ref(0)
// 包含权益
const rights = ref([])
const rights = ref(useTenantStore().tenantInfo?.memberBenefits || [])
const loading = ref(false)
// 编辑模式
@@ -156,7 +157,6 @@
// 生命周期
onMounted(() => {
getRightsList()
checkEditMode()
})
@@ -189,15 +189,6 @@
})
}
// 获取权益列表
function getRightsList() {
rights.value = [
{ value: 'JX', label: '精选题库' },
{ value: 'REAL_EXAM', label: '真实考场' },
{ value: 'SECRET', label: '考前密卷' },
{ value: 'AI', label: 'AI助手' },
]
}
// 车型选择变化处理
function handleCarTypeChange(e) {

View File

@@ -94,6 +94,7 @@
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { getVipTypeList, deleteVipType } from '@/api/member'
import { useTenantStore } from '@/store'
// 会员列表
const memberList = ref([])
@@ -112,13 +113,7 @@
{ value: '1002', label: '摩托车' }
]
const rights = [
{ value: 'JX', label: '精选题库' },
{ value: 'REAL_EXAM', label: '真实考场' },
{ value: 'SECRET', label: '考前密卷' },
{ value: 'AI', label: 'AI助手' },
]
const rights = useTenantStore().tenantInfo?.memberBenefits || []
// 生命周期
onShow(() => {
getMemberList()

View File

@@ -15,8 +15,8 @@
<view class="logo-icon">🏢</view>
</view>
<view class="company-info">
<view class="company-name">顺达驾校管理系统</view>
<view class="company-slogan">专业的驾校管理解决方案</view>
<view class="company-name">{{ config.appInfo.name }}</view>
<view class="company-slogan">专业的刷题小程序管理解决方案</view>
</view>
</view>
@@ -47,7 +47,7 @@
<view class="feature-icon">📊</view>
<view class="feature-info">
<view class="feature-title">统计分析</view>
<view class="feature-desc">实时查看驾校运营数据掌握业务动态</view>
<view class="feature-desc">实时查看运营数据掌握业务动态</view>
</view>
</view>
<view class="feature-item">
@@ -57,18 +57,11 @@
<view class="feature-desc">管理会员信息支持会员赠送和续费</view>
</view>
</view>
<view class="feature-item">
<view class="feature-icon">🎓</view>
<view class="feature-info">
<view class="feature-title">学员管理</view>
<view class="feature-desc">监控学员学习情况提供个性化学习建议</view>
</view>
</view>
<view class="feature-item">
<view class="feature-icon">📈</view>
<view class="feature-info">
<view class="feature-title">分销系统</view>
<view class="feature-desc">支持分销员管理提高驾校招生效率</view>
<view class="feature-desc">支持分销员管理提高推广效率</view>
</view>
</view>
<view class="feature-item">
@@ -88,29 +81,22 @@
<view class="contact-item">
<view class="contact-icon">📞</view>
<view class="contact-info">
<view class="contact-label">客服电话</view>
<view class="contact-value">400-123-4567</view>
<view class="contact-label">联系电话</view>
<view class="contact-value">{{ tentantInfo.contactPhone }}</view>
</view>
</view>
<view class="contact-item">
<view class="contact-icon">📧</view>
<view class="contact-icon">👤</view>
<view class="contact-info">
<view class="contact-label">客服邮箱</view>
<view class="contact-value">support@shunda.com</view>
</view>
</view>
<view class="contact-item">
<view class="contact-icon">🌐</view>
<view class="contact-info">
<view class="contact-label">官方网站</view>
<view class="contact-value">www.shunda.com</view>
<view class="contact-label">联系人</view>
<view class="contact-value">{{ tentantInfo.contactPerson }}</view>
</view>
</view>
<view class="contact-item">
<view class="contact-icon">📍</view>
<view class="contact-info">
<view class="contact-label">公司地址</view>
<view class="contact-value">北京市朝阳区建国路88号</view>
<view class="contact-value">{{ tentantInfo.address }}</view>
</view>
</view>
</view>
@@ -118,24 +104,28 @@
<!-- 版权信息 -->
<view class="copyright-section">
<view class="copyright-text">© 2026 顺达驾校管理系统</view>
<view class="copyright-text">© 2026 {{ tentantInfo.name }}</view>
<view class="copyright-desc">All Rights Reserved</view>
<view class="copyright-policy">
<!-- <view class="copyright-policy">
<view class="policy-item" @click="viewPrivacyPolicy">隐私政策</view>
<view class="policy-divider">|</view>
<view class="policy-item" @click="viewTermsOfService">服务条款</view>
</view>
</view> -->
</view>
</view>
</template>
<script setup>
import { ref } from "vue"
import { useTenantStore } from "@/store/modules/tenant"
import config from "@/config"
// 应用信息
const appVersion = ref('v1.0.0')
const updateTime = ref('2026-01-29')
const appVersion = ref(config.appInfo.version)
const updateTime = ref(config.appInfo.updateTime)
const tentantInfo = useTenantStore().tenantInfo
// 返回上一页
function goBack() {
uni.navigateBack({ delta: 1 })
@@ -247,6 +237,7 @@
font-weight: bold;
color: #303133;
margin-bottom: 16rpx;
text-align: center;
}
.company-slogan {
@@ -445,153 +436,6 @@
color: #909399;
}
/* 平板响应式 */
@media screen and (min-width: 768px) {
.about-us-container {
max-width: 900px;
margin: 0 auto;
width: 100%;
}
.page-header {
height: 140rpx;
padding: 0 48rpx;
}
.header-title {
font-size: 36rpx;
}
.back-icon {
font-size: 48rpx;
}
.company-section {
margin: 24rpx;
padding: 80rpx 40rpx;
}
.company-logo {
width: 180rpx;
height: 180rpx;
border-radius: 90rpx;
margin-bottom: 40rpx;
}
.logo-icon {
font-size: 90rpx;
}
.company-name {
font-size: 40rpx;
margin-bottom: 20rpx;
}
.company-slogan {
font-size: 28rpx;
}
.version-section {
margin: 0 24rpx 24rpx;
padding: 40rpx;
}
.version-item {
padding: 24rpx 0;
}
.version-label {
font-size: 32rpx;
}
.version-value {
font-size: 32rpx;
}
.features-section {
margin: 0 24rpx 24rpx;
padding: 40rpx;
}
.section-title {
font-size: 36rpx;
margin-bottom: 40rpx;
}
.features-list {
grid-template-columns: repeat(auto-fill, minmax(400rpx, 1fr));
gap: 32rpx;
}
.feature-item {
padding: 32rpx;
border-radius: 16rpx;
}
.feature-icon {
font-size: 48rpx;
margin-right: 20rpx;
}
.feature-title {
font-size: 32rpx;
margin-bottom: 12rpx;
}
.feature-desc {
font-size: 28rpx;
}
.contact-section {
margin: 0 24rpx 24rpx;
padding: 40rpx;
}
.contact-list {
gap: 32rpx;
}
.contact-item {
padding: 32rpx;
border-radius: 16rpx;
}
.contact-icon {
font-size: 48rpx;
margin-right: 24rpx;
}
.contact-label {
font-size: 28rpx;
margin-bottom: 8rpx;
}
.contact-value {
font-size: 32rpx;
}
.copyright-section {
padding: 60rpx 40rpx;
}
.copyright-text {
font-size: 32rpx;
margin-bottom: 12rpx;
}
.copyright-desc {
font-size: 28rpx;
margin-bottom: 32rpx;
}
.policy-item {
font-size: 28rpx;
}
.policy-divider {
font-size: 28rpx;
}
}
/* 大屏设备响应式 */
@media screen and (min-width: 1024px) {

View File

@@ -1,187 +0,0 @@
<template>
<view class="about-container">
<view class="header-section text-center">
<image style="width: 150rpx;height: 150rpx;" src="/static/logo200.png" mode="widthFix">
</image>
<uni-title type="h2" title="若依移动端"></uni-title>
</view>
<view class="content-section">
<view class="menu-list">
<view class="list-cell list-cell-arrow">
<view class="menu-item-box">
<view>版本信息</view>
<view class="text-right">v{{version}}</view>
</view>
</view>
<view class="list-cell list-cell-arrow">
<view class="menu-item-box">
<view>官方邮箱</view>
<view class="text-right">ruoyi@xx.com</view>
</view>
</view>
<view class="list-cell list-cell-arrow">
<view class="menu-item-box">
<view>服务热线</view>
<view class="text-right">400-999-9999</view>
</view>
</view>
<view class="list-cell list-cell-arrow">
<view class="menu-item-box">
<view>公司网站</view>
<view class="text-right">
<uni-link :href="url" :text="url" showUnderLine="false"></uni-link>
</view>
</view>
</view>
</view>
</view>
<view class="copyright">
<view>Copyright &copy; 2025 ruoyi.vip All Rights Reserved.</view>
</view>
</view>
</template>
<script setup>
import { useConfigStore } from '@/store'
const url = useConfigStore().config.appInfo.site_url
const version = useConfigStore().config.appInfo.version
</script>
<style lang="scss" scoped>
page {
background-color: #f8f8f8;
}
.about-container {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.header-section {
display: flex;
padding: 30rpx 0 0;
flex-direction: column;
align-items: center;
background-color: #fff;
padding: 60rpx 24rpx 40rpx;
}
.header-section image {
width: 150rpx;
height: 150rpx;
margin-bottom: 24rpx;
}
.content-section {
flex: 1;
background-color: #fff;
margin: 16rpx 0;
}
.menu-list {
padding: 0 24rpx;
}
.list-cell {
border-bottom: 1rpx solid #f0f0f0;
}
.menu-item-box {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
padding: 28rpx 0;
color: #303133;
}
.text-right {
color: #606266;
}
.copyright {
margin-top: 50rpx;
text-align: center;
line-height: 60rpx;
color: #999;
font-size: 24rpx;
padding-bottom: 40rpx;
}
/* 平板响应式 */
@media screen and (min-width: 768px) {
.about-container {
max-width: 900px;
margin: 0 auto;
width: 100%;
background-color: #fff;
}
.header-section {
padding: 60rpx 32rpx 40rpx;
margin-bottom: 0;
}
.header-section image {
width: 180rpx;
height: 180rpx;
margin-bottom: 32rpx;
}
.content-section {
margin: 0;
padding: 0 32rpx;
}
.menu-list {
padding: 0;
}
.menu-item-box {
font-size: 32rpx;
padding: 32rpx 0;
}
.copyright {
margin-top: 80rpx;
font-size: 26rpx;
padding-bottom: 60rpx;
}
}
/* 大屏设备响应式 */
@media screen and (min-width: 1024px) {
.about-container {
max-width: 1000px;
}
.header-section {
padding: 80rpx 48rpx 60rpx;
}
.header-section image {
width: 200rpx;
height: 200rpx;
margin-bottom: 40rpx;
}
.content-section {
padding: 0 48rpx;
}
.menu-item-box {
font-size: 36rpx;
padding: 36rpx 0;
}
.copyright {
margin-top: 100rpx;
font-size: 28rpx;
padding-bottom: 80rpx;
}
}
</style>

View File

@@ -69,7 +69,7 @@
</view>
<!-- 联系客服 -->
<view class="contact-section">
<!-- <view class="contact-section">
<view class="section-title">联系客服</view>
<view class="contact-info">
<view class="contact-item">
@@ -105,7 +105,7 @@
</view>
</view>
</view>
</view>
</view> -->
</view>
</template>
@@ -524,149 +524,6 @@
color: #909399;
}
/* 平板响应式 */
@media screen and (min-width: 768px) {
.help-center-container {
max-width: 900px;
margin: 0 auto;
width: 100%;
}
.page-header {
height: 140rpx;
padding: 0 48rpx;
}
.header-title {
font-size: 36rpx;
}
.back-icon {
font-size: 48rpx;
}
.faq-section {
margin: 24rpx;
padding: 40rpx;
}
.guide-section {
margin: 0 24rpx 24rpx;
padding: 40rpx;
}
.contact-section {
margin: 0 24rpx 32rpx;
padding: 40rpx;
}
.section-header {
margin-bottom: 40rpx;
}
.section-title {
font-size: 36rpx;
}
.search-input {
width: 350rpx;
height: 72rpx;
padding: 0 32rpx;
font-size: 28rpx;
}
.faq-list {
gap: 24rpx;
}
.faq-item {
border-radius: 16rpx;
}
.faq-header {
padding: 32rpx;
}
.faq-question {
font-size: 32rpx;
}
.faq-icon {
font-size: 28rpx;
margin-left: 24rpx;
}
.faq-answer {
padding: 0 32rpx 32rpx;
font-size: 28rpx;
}
.guide-list {
gap: 24rpx;
margin-top: 32rpx;
}
.guide-item {
padding: 32rpx;
border-radius: 16rpx;
}
.guide-icon {
font-size: 56rpx;
margin-right: 32rpx;
}
.guide-title {
font-size: 32rpx;
margin-bottom: 12rpx;
}
.guide-desc {
font-size: 28rpx;
}
.guide-arrow {
font-size: 36rpx;
}
.contact-info {
gap: 32rpx;
margin-top: 32rpx;
}
.contact-item {
padding: 32rpx;
border-radius: 16rpx;
}
.contact-icon {
font-size: 56rpx;
margin-right: 32rpx;
}
.contact-label {
font-size: 28rpx;
margin-bottom: 8rpx;
}
.contact-value {
font-size: 32rpx;
}
.contact-action {
padding: 16rpx 32rpx;
font-size: 28rpx;
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 32rpx;
}
.empty-text {
font-size: 32rpx;
}
}
/* 大屏设备响应式 */
@media screen and (min-width: 1024px) {

View File

@@ -9,8 +9,7 @@
<view class="user-details">
<view class="user-name">{{ name }}</view>
<view class="user-role">
<text v-if="userRole === 'admin'">驾校管理员</text>
<text v-else>分销员</text>
<text>{{ userRole?.name || '未分配角色' }}</text>
</view>
</view>
</view>
@@ -30,22 +29,12 @@
</view>
</view>
<!-- 角色切换 -->
<view v-if="userRole === 'admin'" class="role-switch">
<view class="role-tab" :class="{ active: currentView === 'admin' }" @click="switchView('admin')">
管理员功能
</view>
<view class="role-tab" :class="{ active: currentView === 'distributor' }" @click="switchView('distributor')">
分销员功能
</view>
</view>
<!-- 管理员功能 -->
<view v-if="currentView === 'admin'" class="feature-section">
<view v-if="checkPermi(['mine:school:index'])" class="feature-section">
<view class="section-title">驾校管理</view>
<view class="feature-grid">
<!-- 驾校信息维护 -->
<view class="feature-card" @click="goToSchoolInfo">
<view v-if="checkPermi(['mine:school:info'])" class="feature-card" @click="goToSchoolInfo">
<view class="feature-icon school-icon">
<view class="icon-text">🏢</view>
</view>
@@ -56,7 +45,7 @@
</view>
<!-- 自定义字段配置 -->
<view class="feature-card" @click="goToCustomFields">
<view v-if="checkPermi(['mine:school:diyParam'])" class="feature-card" @click="goToCustomFields">
<view class="feature-icon field-icon">
<view class="icon-text">📝</view>
</view>
@@ -67,7 +56,7 @@
</view>
<!-- 数据相关 -->
<view class="feature-card" @click="goToDataLogs">
<view v-if="checkPermi(['mine:school:dataLog'])" class="feature-card" @click="goToDataLogs">
<view class="feature-icon data-icon">
<view class="icon-text">📋</view>
</view>
@@ -80,11 +69,11 @@
</view>
<!-- 分销员功能 -->
<view v-else class="feature-section">
<view v-if="checkPermi(['mine:selfSetting:index'])" class="feature-section">
<view class="section-title">个人设置</view>
<view class="feature-grid">
<!-- 个人资料修改 -->
<view class="feature-card" @click="goToPersonalInfo">
<view v-if="checkPermi(['mine:selfSetting:selfInfo'])" class="feature-card" @click="goToPersonalInfo">
<view class="feature-icon profile-icon">
<view class="icon-text">👤</view>
</view>
@@ -95,7 +84,7 @@
</view>
<!-- 密码修改 -->
<view class="feature-card" @click="goToPasswordChange">
<view v-if="checkPermi(['mine:selfSetting:password'])" class="feature-card" @click="goToPasswordChange">
<view class="feature-icon password-icon">
<view class="icon-text">🔒</view>
</view>
@@ -104,17 +93,6 @@
<view class="feature-desc">修改登录密码</view>
</view>
</view>
<!-- 驾校信息查看 -->
<view class="feature-card" @click="goToSchoolInfo">
<view class="feature-icon school-icon">
<view class="icon-text">🏢</view>
</view>
<view class="feature-info">
<view class="feature-title">驾校信息查看</view>
<view class="feature-desc">查看本驾校基础信息</view>
</view>
</view>
</view>
</view>
@@ -162,12 +140,13 @@
<script setup>
import { ref, computed, onMounted } from "vue"
import { useUserStore } from '@/store/modules/user'
import { checkPermi } from "@/utils/permission"
const userStore = useUserStore()
// 用户信息
const name = ref('张教练')
const userRole = ref('admin') // 模拟角色,实际应从登录状态获取
const name = computed(() => userStore.name)
const userRole = computed(() => userStore.roles.find(role => role.id == userStore.currentRole))
const currentView = ref('admin')
// 模拟统计数据
@@ -351,31 +330,6 @@
line-height: 1.3;
}
/* 角色切换 */
.role-switch {
display: flex;
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 30rpx;
overflow: hidden;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
}
.role-tab {
flex: 1;
padding: 24rpx;
text-align: center;
font-size: 28rpx;
font-weight: 500;
color: #606266;
transition: all 0.3s ease;
cursor: pointer;
}
.role-tab.active {
background-color: #409eff;
color: #fff;
}
/* 功能区域 */
.feature-section {
@@ -491,94 +445,6 @@
line-height: 1.4;
}
/* 平板响应式 */
@media screen and (min-width: 768px) {
.mine-container {
max-width: 900px;
margin: 0 auto;
padding: 40rpx;
width: 100%;
}
.welcome-section {
padding: 56rpx;
margin-bottom: 40rpx;
}
.avatar {
width: 144rpx;
height: 144rpx;
margin-right: 32rpx;
}
.avatar-icon {
font-size: 72rpx;
}
.user-name {
font-size: 44rpx;
}
.user-role {
font-size: 28rpx;
}
.user-stats {
padding-top: 32rpx;
}
.stat-value {
font-size: 36rpx;
}
.stat-label {
font-size: 24rpx;
}
.role-switch {
margin-bottom: 40rpx;
}
.role-tab {
padding: 28rpx;
font-size: 32rpx;
}
.feature-section {
margin-bottom: 40rpx;
}
.section-title {
font-size: 36rpx;
margin-bottom: 32rpx;
}
.feature-grid {
gap: 24rpx;
}
.feature-card {
padding: 32rpx;
}
.feature-icon {
width: 100rpx;
height: 100rpx;
margin-right: 24rpx;
}
.icon-text {
font-size: 44rpx;
}
.feature-title {
font-size: 32rpx;
}
.feature-desc {
font-size: 24rpx;
}
}
/* 大屏设备响应式 */
@media screen and (min-width: 1024px) {

View File

@@ -14,13 +14,12 @@
<!-- 个人信息表单 -->
<view class="form-section">
<view class="avatar-section">
<view class="avatar-preview" @click="chooseAvatar">
<view class="avatar-preview">
<view v-if="userInfo.avatar" class="avatar-img">
<image :src="userInfo.avatar" mode="aspectFill"></image>
</view>
<view v-else class="avatar-placeholder">
<view class="avatar-icon">{{ userInitial }}</view>
<view class="avatar-tip">点击更换头像</view>
</view>
</view>
</view>
@@ -49,65 +48,6 @@
/>
</view>
</view>
<view class="form-group">
<view class="form-label">邮箱</view>
<view class="form-control">
<input
v-model="userInfo.email"
class="form-input"
placeholder="请输入邮箱"
maxlength="50"
type="email"
/>
</view>
</view>
<view class="form-group">
<view class="form-label">性别</view>
<view class="form-control">
<picker
:value="getGenderIndex()"
:range="genders"
@change="onGenderChange"
class="form-picker"
>
<view class="picker-display">{{ userInfo.gender || '请选择性别' }}</view>
</picker>
</view>
</view>
<view class="form-group">
<view class="form-label">所属驾校</view>
<view class="form-control">
<view class="form-text">{{ userInfo.school || '顺达驾校' }}</view>
</view>
</view>
<view class="form-group">
<view class="form-label">职位</view>
<view class="form-control">
<input
v-model="userInfo.position"
class="form-input"
placeholder="请输入职位"
maxlength="20"
/>
</view>
</view>
<view class="form-group">
<view class="form-label">简介</view>
<view class="form-control">
<textarea
v-model="userInfo.bio"
class="form-textarea"
placeholder="请输入个人简介"
maxlength="200"
rows="3"
></textarea>
</view>
</view>
</view>
<!-- 账号信息 -->
@@ -141,7 +81,6 @@
const userInfo = ref({
name: '李教练',
phone: '13900139000',
email: 'li@shunda.com',
gender: '男',
school: '顺达驾校',
position: '科目二教练',
@@ -277,7 +216,6 @@
/* 表单区域 */
.form-section {
flex: 1;
padding: 32rpx;
background-color: #fff;
margin: 16rpx;
@@ -466,108 +404,6 @@
font-weight: 500;
}
/* 平板响应式 */
@media screen and (min-width: 768px) {
.personal-info-container {
max-width: 900px;
margin: 0 auto;
width: 100%;
}
.page-header {
height: 140rpx;
padding: 0 48rpx;
}
.header-title {
font-size: 36rpx;
}
.back-icon {
font-size: 48rpx;
}
.save-btn {
font-size: 32rpx;
}
.form-section {
margin: 24rpx;
padding: 40rpx;
}
.avatar-section {
margin-bottom: 48rpx;
}
.avatar-preview {
width: 240rpx;
height: 240rpx;
}
.avatar-icon {
font-size: 96rpx;
}
.avatar-tip {
font-size: 24rpx;
}
.form-group {
margin-bottom: 40rpx;
}
.form-label {
font-size: 32rpx;
margin-bottom: 16rpx;
}
.form-input {
height: 96rpx;
font-size: 32rpx;
padding: 0 32rpx;
}
.form-textarea {
min-height: 200rpx;
font-size: 32rpx;
padding: 32rpx;
}
.form-text {
height: 96rpx;
font-size: 32rpx;
padding: 0 32rpx;
}
.picker-display {
font-size: 32rpx;
padding: 0 32rpx;
}
.account-section {
margin: 0 24rpx 32rpx;
padding: 40rpx;
}
.section-title {
font-size: 36rpx;
margin-bottom: 32rpx;
}
.account-info {
gap: 24rpx;
}
.info-item {
padding: 24rpx 0;
}
.info-label,
.info-value {
font-size: 32rpx;
}
}
/* 大屏设备响应式 */
@media screen and (min-width: 1024px) {

View File

@@ -17,7 +17,7 @@
<view class="form-label">驾校名称</view>
<view class="form-control">
<input
v-model="schoolInfo.name"
v-model="tenantInfo.name"
class="form-input"
placeholder="请输入驾校名称"
maxlength="50"
@@ -29,7 +29,7 @@
<view class="form-label">联系人</view>
<view class="form-control">
<input
v-model="schoolInfo.contact"
v-model="tenantInfo.contactPerson"
class="form-input"
placeholder="请输入联系人姓名"
maxlength="20"
@@ -41,7 +41,7 @@
<view class="form-label">联系电话</view>
<view class="form-control">
<input
v-model="schoolInfo.phone"
v-model="tenantInfo.contactPhone"
class="form-input"
placeholder="请输入联系电话"
maxlength="15"
@@ -54,7 +54,7 @@
<view class="form-label">地址</view>
<view class="form-control">
<input
v-model="schoolInfo.address"
v-model="tenantInfo.address"
class="form-input"
placeholder="请输入驾校地址"
maxlength="100"
@@ -63,41 +63,43 @@
</view>
<view class="form-group">
<view class="form-label">邮箱</view>
<view class="form-label">驾校简介</view>
<view class="form-control">
<input
v-model="schoolInfo.email"
v-model="tenantInfo.introduction"
class="form-input"
placeholder="请输入邮箱地址"
maxlength="50"
type="email"
placeholder="请输入驾校简介网页地址"
/>
</view>
</view>
<view class="form-group">
<view class="form-label">经营范围</view>
<view class="form-label">驾校场地</view>
<view class="form-control">
<textarea
v-model="schoolInfo.businessScope"
class="form-textarea"
placeholder="请输入经营范围"
maxlength="200"
rows="3"
></textarea>
<input
v-model="tenantInfo.venue"
class="form-input"
placeholder="请输入驾校场地网页地址"
/>
</view>
</view>
<view class="form-group">
<view class="form-label">驾校简介</view>
<view class="form-label">驾校教练</view>
<view class="form-control">
<textarea
v-model="schoolInfo.description"
class="form-textarea"
placeholder="请输入驾校简介"
maxlength="500"
rows="4"
></textarea>
<input
v-model="tenantInfo.coach"
class="form-input"
placeholder="请输入驾校教练网页地址"
/>
</view>
</view>
<view class="form-group">
<view class="form-label">驾校约车</view>
<view class="form-control">
<input
v-model="tenantInfo.booking"
class="form-input"
placeholder="请输入驾校约车网页地址"
/>
</view>
</view>
</view>
@@ -107,7 +109,6 @@
<view class="tips-title">注意事项</view>
<view class="tips-content">
<view class="tip-item"> 驾校名称联系人和联系电话为必填项</view>
<view class="tip-item"> 核心功能开关由平台控制不可在此修改</view>
<view class="tip-item"> 修改信息后需点击保存按钮生效</view>
<view class="tip-item"> 请确保填写的信息真实有效</view>
</view>
@@ -117,22 +118,10 @@
<script setup>
import { ref, onMounted } from "vue"
import { useTenantStore } from '@/store/modules/tenant'
// 驾校信息
const schoolInfo = ref({
name: '顺达驾校',
contact: '张校长',
phone: '13800138000',
address: '北京市朝阳区建国路88号',
email: 'contact@shunda.com',
businessScope: '机动车驾驶员培训(小型汽车、大型货车)',
description: '顺达驾校成立于2005年是北京市知名的驾驶员培训机构拥有专业的教练团队和完善的教学设施致力于为学员提供优质的驾驶培训服务。'
})
onMounted(() => {
// 实际项目中应从接口获取驾校信息
// loadSchoolInfo()
})
const tenantInfo = ref(useTenantStore().tenantInfo)
// 返回上一页
function goBack() {
@@ -142,27 +131,45 @@
// 保存驾校信息
function saveInfo() {
// 表单验证
if (!schoolInfo.value.name) {
if (!tenantInfo.value.name) {
uni.showToast({ title: '请输入驾校名称', icon: 'none' })
return
}
if (!schoolInfo.value.contact) {
if (!tenantInfo.value.contactPerson) {
uni.showToast({ title: '请输入联系人', icon: 'none' })
return
}
if (!schoolInfo.value.phone) {
if (!tenantInfo.value.contactPhone) {
uni.showToast({ title: '请输入联系电话', icon: 'none' })
return
}
// 判断四个网页地址是否https开头可为空
if (tenantInfo.value.introduction && !tenantInfo.value.introduction.startsWith('https://')) {
uni.showToast({ title: '请输入正确的驾校简介网页地址,必须以https开头', icon: 'none' })
return
}
if (tenantInfo.value.venue && !tenantInfo.value.venue.startsWith('https://')) {
uni.showToast({ title: '请输入正确的驾校场地网页地址,必须以https开头', icon: 'none' })
return
}
if (tenantInfo.value.coach && !tenantInfo.value.coach.startsWith('https://')) {
uni.showToast({ title: '请输入正确的驾校教练网页地址,必须以https开头', icon: 'none' })
return
}
if (tenantInfo.value.booking && !tenantInfo.value.booking.startsWith('https://')) {
uni.showToast({ title: '请输入正确的驾校约车网页地址,必须以https开头', icon: 'none' })
return
}
// 实际项目中应调用接口保存信息
uni.showLoading({ title: '保存中...' })
setTimeout(() => {
useTenantStore().updateTenantInfo(tenantInfo.value).then(() => {
uni.hideLoading()
uni.showToast({ title: '保存成功', icon: 'success' })
goBack()
}, 1000)
})
}
</script>
@@ -318,75 +325,6 @@
margin-bottom: 8rpx;
}
/* 平板响应式 */
@media screen and (min-width: 768px) {
.school-info-container {
max-width: 900px;
margin: 0 auto;
width: 100%;
}
.page-header {
height: 140rpx;
padding: 0 48rpx;
}
.header-title {
font-size: 36rpx;
}
.back-icon {
font-size: 48rpx;
}
.save-btn {
font-size: 32rpx;
}
.form-section {
margin: 24rpx;
padding: 40rpx;
}
.form-group {
margin-bottom: 40rpx;
}
.form-label {
font-size: 32rpx;
margin-bottom: 16rpx;
}
.form-input {
height: 96rpx;
font-size: 32rpx;
padding: 0 32rpx;
}
.form-textarea {
min-height: 200rpx;
font-size: 32rpx;
padding: 32rpx;
}
.tips-section {
margin: 0 24rpx 32rpx;
padding: 40rpx;
}
.tips-title {
font-size: 32rpx;
margin-bottom: 24rpx;
}
.tips-content {
font-size: 28rpx;
}
.tip-item {
margin-bottom: 12rpx;
}
}
/* 大屏设备响应式 */
@media screen and (min-width: 1024px) {

View File

@@ -1,9 +1,10 @@
import { createPinia } from 'pinia'
import { useUserStore } from './modules/user'
import { useConfigStore } from './modules/config'
import { createPinia } from 'pinia';
import { useUserStore } from './modules/user';
import { useConfigStore } from './modules/config';
import { useTenantStore } from './modules/tenant';
const pinia = createPinia()
const pinia = createPinia();
export default pinia
export default pinia;
export { useUserStore, useConfigStore }
export { useUserStore, useConfigStore, useTenantStore };

View File

@@ -0,0 +1,45 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
import config from '@/config';
import storage from '@/utils/storage';
import constant from '@/utils/constant';
import { isEmpty } from '@/utils/validate';
import { getTenantInfo, updateTenant } from '@/api/system/tenant';
export const useTenantStore = defineStore('tenant', () => {
const tenantInfo = ref(storage.get(constant.tenantInfo));
const SET_TENANT_INFO = val => {
tenantInfo.value = val;
storage.set(constant.tenantInfo, val);
};
const getTenant = () => {
return new Promise(resolve => {
getTenantInfo({ id: config.appInfo.tenantId }).then(res => {
if (!isEmpty(res.data)) {
SET_TENANT_INFO(res.data);
}
resolve(res);
});
});
};
const updateTenantInfo = data => {
return new Promise(resolve => {
updateTenant(data).then(res => {
if (!isEmpty(res.data)) {
getTenant();
}
resolve(res);
});
});
};
return {
tenantInfo,
SET_TENANT_INFO,
getTenant,
updateTenantInfo
};
});

View File

@@ -14,6 +14,7 @@ export const useUserStore = defineStore('user', () => {
const token = ref(getToken());
const id = ref(storage.get(constant.id));
const name = ref(storage.get(constant.name));
const currentRole = ref(storage.get('currentRole'));
const avatar = ref(storage.get(constant.avatar));
const roles = ref(storage.get(constant.roles));
const permissions = ref(storage.get(constant.permissions));
@@ -29,6 +30,10 @@ export const useUserStore = defineStore('user', () => {
name.value = val;
storage.set(constant.name, val);
};
const SET_CURRENT_ROLE = val => {
currentRole.value = val;
storage.set('currentRole', val);
};
const SET_AVATAR = val => {
avatar.value = val;
storage.set(constant.avatar, val);
@@ -69,8 +74,9 @@ export const useUserStore = defineStore('user', () => {
if (!isHttp(avatar)) {
avatar = isEmpty(avatar) ? defAva : baseUrl + avatar;
}
const userid = isEmpty(user) || isEmpty(user.userId) ? '' : user.userId;
const username = isEmpty(user) || isEmpty(user.userName) ? '' : user.userName;
const userid = isEmpty(user) || isEmpty(user.id) ? '' : user.id;
const username = isEmpty(user) || isEmpty(user.nickname) ? '' : user.nickname;
const role = isEmpty(user) || isEmpty(user.currentRole) ? '' : user.currentRole;
if (res.data.roles && res.data.roles.length > 0) {
SET_ROLES(res.data.roles);
SET_PERMISSIONS(res.data.permissions);
@@ -80,6 +86,7 @@ export const useUserStore = defineStore('user', () => {
SET_ID(userid);
SET_NAME(username);
SET_AVATAR(avatar);
SET_CURRENT_ROLE(role);
resolve(res);
})
.catch(error => {
@@ -95,6 +102,7 @@ export const useUserStore = defineStore('user', () => {
.then(() => {
SET_TOKEN('');
SET_ROLES([]);
SET_CURRENT_ROLE('');
SET_PERMISSIONS([]);
removeToken();
storage.clean();
@@ -110,6 +118,7 @@ export const useUserStore = defineStore('user', () => {
token,
id,
name,
currentRole,
avatar,
roles,
permissions,

View File

@@ -3,7 +3,8 @@ const constant = {
id: 'user_id',
name: 'user_name',
roles: 'user_roles',
permissions: 'user_permissions'
}
permissions: 'user_permissions',
tenantInfo: 'tenant_info'
};
export default constant
export default constant;

View File

@@ -6,13 +6,15 @@ import { toast, showConfirm, tansParams } from '@/utils/common';
let timeout = 10000;
const baseUrl = config.baseUrl;
const tenantId = config.appInfo.tenantId;
const instanceId = config.appInfo.instanceId;
const request = config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false;
config.header = config.header || {};
config.header['tenant-id'] = 10001;
config.header['instance-id'] = 1038;
config.header['tenant-id'] = tenantId || 10001;
config.header['instance-id'] = instanceId || 1038;
if (getToken() && !isToken) {
config.header['Authorization'] = 'Bearer ' + getToken();
}