13 changed files with 1331 additions and 162 deletions
@ -0,0 +1,194 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\api\controller\student; |
|||
|
|||
use core\base\BaseApiController; |
|||
use app\service\api\student\ContractService; |
|||
|
|||
/** |
|||
* 学员合同管理控制器 |
|||
*/ |
|||
class StudentContract extends BaseApiController |
|||
{ |
|||
/** |
|||
* 获取学员合同列表 |
|||
* @return \think\Response |
|||
*/ |
|||
public function lists() |
|||
{ |
|||
$data = $this->request->params([ |
|||
['student_id', 0], |
|||
['status', ''], |
|||
['page', 1], |
|||
['limit', 10] |
|||
]); |
|||
|
|||
if (empty($data['student_id'])) { |
|||
return fail('学员ID不能为空'); |
|||
} |
|||
|
|||
return success((new ContractService())->getContractList($data)); |
|||
} |
|||
|
|||
/** |
|||
* 获取合同详情 |
|||
* @return \think\Response |
|||
*/ |
|||
public function info() |
|||
{ |
|||
$data = $this->request->params([ |
|||
['contract_id', 0], |
|||
['student_id', 0] |
|||
]); |
|||
|
|||
if (empty($data['contract_id']) || empty($data['student_id'])) { |
|||
return fail('参数错误'); |
|||
} |
|||
|
|||
return success((new ContractService())->getContractDetail($data['contract_id'], $data['student_id'])); |
|||
} |
|||
|
|||
/** |
|||
* 获取合同签署表单配置 |
|||
* @return \think\Response |
|||
*/ |
|||
public function getSignForm() |
|||
{ |
|||
$data = $this->request->params([ |
|||
['contract_id', 0], |
|||
['student_id', 0] |
|||
]); |
|||
|
|||
if (empty($data['contract_id']) || empty($data['student_id'])) { |
|||
return fail('参数错误'); |
|||
} |
|||
|
|||
return success((new ContractService())->getSignForm($data['contract_id'], $data['student_id'])); |
|||
} |
|||
|
|||
/** |
|||
* 提交合同签署 |
|||
* @return \think\Response |
|||
*/ |
|||
public function signContract() |
|||
{ |
|||
$data = $this->request->params([ |
|||
['contract_id', 0], |
|||
['student_id', 0], |
|||
['form_data', []], |
|||
['signature_image', ''] |
|||
]); |
|||
|
|||
if (empty($data['contract_id']) || empty($data['student_id'])) { |
|||
return fail('参数错误'); |
|||
} |
|||
|
|||
if (empty($data['form_data']) && empty($data['signature_image'])) { |
|||
return fail('请填写必要信息或提供签名'); |
|||
} |
|||
|
|||
return success((new ContractService())->signContract($data)); |
|||
} |
|||
|
|||
/** |
|||
* 下载合同文件 |
|||
* @return \think\Response |
|||
*/ |
|||
public function downloadContract() |
|||
{ |
|||
$data = $this->request->params([ |
|||
['contract_id', 0], |
|||
['student_id', 0] |
|||
]); |
|||
|
|||
if (empty($data['contract_id']) || empty($data['student_id'])) { |
|||
return fail('参数错误'); |
|||
} |
|||
|
|||
return success((new ContractService())->downloadContract($data['contract_id'], $data['student_id'])); |
|||
} |
|||
|
|||
/** |
|||
* 获取学员基本信息 |
|||
* @return \think\Response |
|||
*/ |
|||
public function getStudentInfo() |
|||
{ |
|||
$data = $this->request->params([ |
|||
['student_id', 0] |
|||
]); |
|||
|
|||
if (empty($data['student_id'])) { |
|||
return fail('学员ID不能为空'); |
|||
} |
|||
|
|||
return success((new ContractService())->getStudentInfo($data['student_id'])); |
|||
} |
|||
|
|||
/** |
|||
* 直接下载合同文件 |
|||
* @return \think\Response |
|||
*/ |
|||
public function downloadFile() |
|||
{ |
|||
$data = $this->request->params([ |
|||
['contract_id', 0], |
|||
['student_id', 0] |
|||
]); |
|||
|
|||
if (empty($data['contract_id']) || empty($data['student_id'])) { |
|||
return fail('参数错误'); |
|||
} |
|||
|
|||
try { |
|||
$fileInfo = (new ContractService())->downloadContract($data['contract_id'], $data['student_id']); |
|||
|
|||
// 构建文件的实际路径 |
|||
$filePath = ''; |
|||
if ($fileInfo['file_type'] === 'signed') { |
|||
// 已签署文档路径 |
|||
$contractSign = \think\facade\Db::table('school_contract_sign') |
|||
->where([ |
|||
['contract_id', '=', $data['contract_id']], |
|||
['student_id', '=', $data['student_id']], |
|||
['deleted_at', '=', 0] |
|||
]) |
|||
->find(); |
|||
$filePath = public_path() . '/upload/' . $contractSign['sign_file']; |
|||
} else { |
|||
// 模板文档路径 |
|||
$contract = \think\facade\Db::table('school_contract') |
|||
->where('id', $data['contract_id']) |
|||
->find(); |
|||
$filePath = public_path() . '/upload/' . $contract['contract_template']; |
|||
} |
|||
|
|||
// 检查文件是否存在 |
|||
if (!file_exists($filePath)) { |
|||
return fail('文件不存在'); |
|||
} |
|||
|
|||
// 设置下载响应头 |
|||
$response = response()->create('', 'html'); |
|||
$response->header([ |
|||
'Content-Type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
|||
'Content-Disposition' => 'attachment; filename=' . urlencode($fileInfo['file_name']), |
|||
'Content-Length' => filesize($filePath), |
|||
'Cache-Control' => 'private', |
|||
'Pragma' => 'private', |
|||
'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT' |
|||
]); |
|||
|
|||
// 输出文件内容 |
|||
$response->data(file_get_contents($filePath)); |
|||
|
|||
return $response; |
|||
|
|||
} catch (\Exception $e) { |
|||
return fail('下载失败:' . $e->getMessage()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
<?php |
|||
declare(strict_types=1); |
|||
|
|||
namespace app\model\school_approval; |
|||
|
|||
use core\base\BaseModel; |
|||
|
|||
/** |
|||
* 审批历史模型 |
|||
* Class SchoolApprovalHistory |
|||
* @package app\model\school_approval |
|||
*/ |
|||
class SchoolApprovalHistory extends BaseModel |
|||
{ |
|||
/** |
|||
* 数据表主键 |
|||
* @var string |
|||
*/ |
|||
protected $pk = 'id'; |
|||
|
|||
/** |
|||
* 模型名称 |
|||
* @var string |
|||
*/ |
|||
protected $name = 'approval_history'; |
|||
|
|||
// 审批动作常量 |
|||
const ACTION_APPROVE = 'approve'; |
|||
const ACTION_REJECT = 'reject'; |
|||
const ACTION_FORWARD = 'forward'; |
|||
const ACTION_CANCEL = 'cancel'; |
|||
|
|||
// 审批状态常量 |
|||
const STATUS_APPROVED = 'approved'; |
|||
const STATUS_REJECTED = 'rejected'; |
|||
const STATUS_FORWARDED = 'forwarded'; |
|||
const STATUS_CANCELLED = 'cancelled'; |
|||
|
|||
/** |
|||
* 关联审批流程 |
|||
* @return \think\model\relation\BelongsTo |
|||
*/ |
|||
public function approvalProcess() |
|||
{ |
|||
return $this->belongsTo(SchoolApprovalProcess::class, 'process_id', 'id'); |
|||
} |
|||
|
|||
/** |
|||
* 关联参与人(人员表) |
|||
* @return \think\model\relation\BelongsTo |
|||
*/ |
|||
public function participant() |
|||
{ |
|||
return $this->belongsTo('app\model\personnel\Personnel', 'participant_id', 'id'); |
|||
} |
|||
} |
|||
@ -0,0 +1,578 @@ |
|||
<template> |
|||
<view class="contract-form-container"> |
|||
<!-- 自定义导航栏 --> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_back" @click="goBack"> |
|||
<text class="back_icon">‹</text> |
|||
</view> |
|||
<view class="navbar_title">合同签署</view> |
|||
<view class="navbar_action"></view> |
|||
</view> |
|||
|
|||
<!-- 合同信息 --> |
|||
<view class="contract-info"> |
|||
<view class="info-card"> |
|||
<text class="contract-name">{{ contractName }}</text> |
|||
<text class="contract-type">{{ contractInfo.contract_type || '学员课程协议' }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 表单区域 --> |
|||
<view class="form-section" v-if="formFields.length > 0"> |
|||
<view class="section-title">合同信息填写</view> |
|||
|
|||
<view class="form-content"> |
|||
<view v-for="(field, index) in formFields" :key="field.id || index" class="form-field"> |
|||
<!-- 普通输入字段 --> |
|||
<view v-if="field.data_type === 'user_input'" class="input-field"> |
|||
<view class="field-label"> |
|||
<text class="label-text">{{ field.name }}</text> |
|||
<text v-if="field.is_required" class="required-mark">*</text> |
|||
</view> |
|||
|
|||
<!-- 文本输入 --> |
|||
<input |
|||
v-if="field.field_type === 'text'" |
|||
v-model="formData[field.placeholder]" |
|||
:placeholder="field.default_value || `请输入${field.name}`" |
|||
class="form-input" |
|||
/> |
|||
|
|||
<!-- 日期输入 --> |
|||
<picker |
|||
v-if="field.field_type === 'date'" |
|||
mode="date" |
|||
:value="formData[field.placeholder] || getCurrentDate()" |
|||
@change="onDateChange($event, field.placeholder)" |
|||
class="form-picker"> |
|||
<view class="picker-display"> |
|||
{{ formData[field.placeholder] || getCurrentDate() }} |
|||
</view> |
|||
</picker> |
|||
|
|||
<!-- 数字输入 --> |
|||
<input |
|||
v-if="field.field_type === 'number'" |
|||
v-model="formData[field.placeholder]" |
|||
type="number" |
|||
:placeholder="field.default_value || `请输入${field.name}`" |
|||
class="form-input" |
|||
/> |
|||
</view> |
|||
|
|||
<!-- 签名字段 --> |
|||
<view v-if="field.data_type === 'signature'" class="signature-field"> |
|||
<view class="field-label"> |
|||
<text class="label-text">{{ field.name }}</text> |
|||
<text v-if="field.is_required" class="required-mark">*</text> |
|||
</view> |
|||
|
|||
<view class="signature-container"> |
|||
<view v-if="!signatures[field.placeholder]" class="signature-placeholder" @click="goToSignature(field)"> |
|||
<text class="placeholder-icon">✍️</text> |
|||
<text class="placeholder-text">点击进行签名</text> |
|||
</view> |
|||
|
|||
<view v-else class="signature-preview" @click="goToSignature(field)"> |
|||
<image :src="signatures[field.placeholder]" class="signature-image" mode="aspectFit"></image> |
|||
<text class="signature-tip">点击重新签名</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 显示字段 --> |
|||
<view v-if="field.data_type === 'database' || field.data_type === 'system'" class="display-field"> |
|||
<view class="field-label"> |
|||
<text class="label-text">{{ field.name }}</text> |
|||
</view> |
|||
<view class="display-value">{{ field.default_value || '自动填充' }}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 合同条款(如果有) --> |
|||
<view class="terms-section" v-if="contractInfo.contract_content"> |
|||
<view class="section-title">合同条款</view> |
|||
<view class="terms-content"> |
|||
<text class="terms-text">{{ contractInfo.contract_content }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 底部操作按钮 --> |
|||
<view class="footer-actions"> |
|||
<button class="action-btn secondary" @click="goBack">取消</button> |
|||
<button class="action-btn primary" |
|||
@click="submitContract" |
|||
:disabled="!isFormValid || submitting" |
|||
:loading="submitting"> |
|||
{{ submitting ? '提交中...' : '确认签署' }} |
|||
</button> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
contractId: 0, |
|||
studentId: 0, |
|||
contractName: '', |
|||
contractInfo: {}, |
|||
formFields: [], |
|||
formData: {}, |
|||
signatures: {}, |
|||
submitting: false, |
|||
loading: false |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
isFormValid() { |
|||
// 检查必填字段是否都已填写 |
|||
for (let field of this.formFields) { |
|||
if (field.is_required) { |
|||
if (field.data_type === 'signature') { |
|||
if (!this.signatures[field.placeholder]) { |
|||
return false |
|||
} |
|||
} else if (field.data_type === 'user_input') { |
|||
if (!this.formData[field.placeholder] || this.formData[field.placeholder].trim() === '') { |
|||
return false |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
}, |
|||
|
|||
onLoad(options) { |
|||
this.contractId = parseInt(options.contract_id) || 0 |
|||
this.studentId = parseInt(options.student_id) || 0 |
|||
this.contractName = options.contractName ? decodeURIComponent(options.contractName) : '合同签署' |
|||
|
|||
if (this.contractId && this.studentId) { |
|||
this.loadSignForm() |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
onShow() { |
|||
// 从签名页面返回时,检查是否有新的签名数据 |
|||
const pages = getCurrentPages() |
|||
const currentPage = pages[pages.length - 1] |
|||
|
|||
if (currentPage.data && currentPage.data.newSignature) { |
|||
const signData = currentPage.data.newSignature |
|||
this.signatures[signData.fieldPlaceholder] = signData.signatureData |
|||
// 清理临时数据 |
|||
currentPage.data.newSignature = null |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
|
|||
async loadSignForm() { |
|||
this.loading = true |
|||
try { |
|||
const response = await apiRoute.getStudentContractSignForm({ |
|||
contract_id: this.contractId, |
|||
student_id: this.studentId |
|||
}) |
|||
|
|||
if (response.code === 1) { |
|||
this.contractInfo = response.data |
|||
this.formFields = response.data.form_fields || [] |
|||
|
|||
// 初始化表单数据 |
|||
this.formFields.forEach(field => { |
|||
if (field.data_type === 'user_input' && field.default_value) { |
|||
this.$set(this.formData, field.placeholder, field.default_value) |
|||
} |
|||
}) |
|||
|
|||
console.log('表单配置加载成功:', this.formFields) |
|||
} else { |
|||
console.error('API返回错误:', response.msg) |
|||
uni.showToast({ |
|||
title: response.msg || '获取表单配置失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('获取表单配置失败:', error) |
|||
uni.showToast({ |
|||
title: '获取表单配置失败', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.loading = false |
|||
} |
|||
}, |
|||
|
|||
getCurrentDate() { |
|||
const now = new Date() |
|||
const year = now.getFullYear() |
|||
const month = String(now.getMonth() + 1).padStart(2, '0') |
|||
const day = String(now.getDate()).padStart(2, '0') |
|||
return `${year}-${month}-${day}` |
|||
}, |
|||
|
|||
onDateChange(event, fieldPlaceholder) { |
|||
this.$set(this.formData, fieldPlaceholder, event.detail.value) |
|||
}, |
|||
|
|||
goToSignature(field) { |
|||
uni.navigateTo({ |
|||
url: `/pages-common/contract/contract_sign?contract_id=${this.contractId}&field_name=${encodeURIComponent(field.name)}&field_placeholder=${encodeURIComponent(field.placeholder)}` |
|||
}) |
|||
}, |
|||
|
|||
async submitContract() { |
|||
if (!this.isFormValid) { |
|||
uni.showToast({ |
|||
title: '请填写完整信息并完成签名', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.submitting = true |
|||
|
|||
try { |
|||
// 准备提交数据 |
|||
const submitData = { |
|||
contract_id: this.contractId, |
|||
student_id: this.studentId, |
|||
form_data: { ...this.formData }, |
|||
signature_image: '' |
|||
} |
|||
|
|||
// 添加签名数据到form_data中 |
|||
for (let field of this.formFields) { |
|||
if (field.data_type === 'signature' && this.signatures[field.placeholder]) { |
|||
submitData.form_data[field.placeholder] = this.signatures[field.placeholder] |
|||
|
|||
// 如果是主要签名,设置为signature_image |
|||
if (!submitData.signature_image) { |
|||
submitData.signature_image = this.signatures[field.placeholder] |
|||
} |
|||
} |
|||
} |
|||
|
|||
console.log('提交合同数据:', submitData) |
|||
|
|||
const response = await apiRoute.signStudentContract(submitData) |
|||
|
|||
if (response.code === 1) { |
|||
uni.showToast({ |
|||
title: '合同签署成功', |
|||
icon: 'success', |
|||
duration: 2000 |
|||
}) |
|||
|
|||
setTimeout(() => { |
|||
// 返回合同列表页面 |
|||
uni.navigateBack({ |
|||
delta: 2 |
|||
}) |
|||
}, 2000) |
|||
} else { |
|||
throw new Error(response.msg || '合同签署失败') |
|||
} |
|||
} catch (error) { |
|||
console.error('提交合同失败:', error) |
|||
uni.showToast({ |
|||
title: error.message || '网络错误,请稍后重试', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.submitting = false |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.contract-form-container { |
|||
min-height: 100vh; |
|||
background: #f8f9fa; |
|||
padding-bottom: 120rpx; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
background: #29D3B4; |
|||
padding: 40rpx 32rpx 20rpx; |
|||
|
|||
// 小程序端适配状态栏 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 80rpx; |
|||
// #endif |
|||
|
|||
.navbar_back { |
|||
width: 60rpx; |
|||
|
|||
.back_icon { |
|||
color: #fff; |
|||
font-size: 40rpx; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
.navbar_title { |
|||
color: #fff; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.navbar_action { |
|||
width: 60rpx; |
|||
} |
|||
} |
|||
|
|||
// 合同信息 |
|||
.contract-info { |
|||
padding: 20rpx; |
|||
|
|||
.info-card { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx; |
|||
text-align: center; |
|||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08); |
|||
|
|||
.contract-name { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.contract-type { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 表单区域 |
|||
.form-section { |
|||
margin: 20rpx; |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08); |
|||
|
|||
.section-title { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
padding: 24rpx 32rpx 16rpx; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.form-content { |
|||
padding: 24rpx 32rpx 32rpx; |
|||
|
|||
.form-field { |
|||
margin-bottom: 32rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.field-label { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 12rpx; |
|||
|
|||
.label-text { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.required-mark { |
|||
color: #ff4757; |
|||
font-size: 26rpx; |
|||
margin-left: 4rpx; |
|||
} |
|||
} |
|||
|
|||
.form-input { |
|||
width: 100%; |
|||
height: 80rpx; |
|||
background: #f8f9fa; |
|||
border-radius: 8rpx; |
|||
padding: 0 20rpx; |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.form-picker { |
|||
width: 100%; |
|||
height: 80rpx; |
|||
background: #f8f9fa; |
|||
border-radius: 8rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.picker-display { |
|||
padding: 0 20rpx; |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
flex: 1; |
|||
} |
|||
} |
|||
|
|||
.display-value { |
|||
height: 80rpx; |
|||
background: #f0f0f0; |
|||
border-radius: 8rpx; |
|||
padding: 0 20rpx; |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 签名字段样式 |
|||
.signature-field { |
|||
.signature-container { |
|||
background: #f8f9fa; |
|||
border-radius: 8rpx; |
|||
min-height: 120rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
border: 2rpx dashed #ddd; |
|||
|
|||
.signature-placeholder { |
|||
text-align: center; |
|||
color: #999; |
|||
|
|||
.placeholder-icon { |
|||
font-size: 40rpx; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.placeholder-text { |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
|
|||
.signature-preview { |
|||
width: 100%; |
|||
text-align: center; |
|||
|
|||
.signature-image { |
|||
width: 200rpx; |
|||
height: 80rpx; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.signature-tip { |
|||
font-size: 22rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 合同条款 |
|||
.terms-section { |
|||
margin: 20rpx; |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08); |
|||
|
|||
.section-title { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
padding: 24rpx 32rpx 16rpx; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.terms-content { |
|||
padding: 24rpx 32rpx 32rpx; |
|||
|
|||
.terms-text { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
line-height: 1.6; |
|||
white-space: pre-line; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 底部操作按钮 |
|||
.footer-actions { |
|||
position: fixed; |
|||
bottom: 0; |
|||
left: 0; |
|||
right: 0; |
|||
background: #fff; |
|||
padding: 24rpx 32rpx; |
|||
border-top: 1rpx solid #f0f0f0; |
|||
display: flex; |
|||
gap: 24rpx; |
|||
z-index: 100; |
|||
|
|||
.action-btn { |
|||
flex: 1; |
|||
height: 88rpx; |
|||
border-radius: 12rpx; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
border: none; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
&.primary { |
|||
background: #29D3B4; |
|||
color: #fff; |
|||
|
|||
&:active:not(:disabled) { |
|||
background: #26c6a0; |
|||
} |
|||
|
|||
&:disabled { |
|||
background: #ccc; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
&.secondary { |
|||
background: #f8f9fa; |
|||
color: #666; |
|||
|
|||
&:active { |
|||
background: #e9ecef; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue