feat:新增授权管理子页面:列表页面,新增授权页面
This commit is contained in:
1 parent
c9fed52b29
commit
8f0e20f46a
2 files changed
+656
-21
No files matched your search
@@ -1,5 +1,164 @@
|
||||
<template>
|
||||
<!-- 页面模式 -->
|
||||
<div v-if="isPageMode" class="license-form-page">
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<h1 class="page-title">{{ isEdit ? '编辑授权' : t('pages.licenses.actions.createLicense') }}</h1>
|
||||
<div class="header-actions">
|
||||
<el-button @click="handleBack">
|
||||
{{ t('customers.actions.cancel') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="submitting"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
{{ t('customers.actions.save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-content">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="120px"
|
||||
class="license-form"
|
||||
>
|
||||
<div class="form-section">
|
||||
<h3 class="section-title">{{ t('pages.licenses.form.basicInfo') }}</h3>
|
||||
|
||||
<el-form-item
|
||||
:label="t('pages.licenses.form.selectCustomer')"
|
||||
prop="customer_id"
|
||||
>
|
||||
<el-select
|
||||
v-model="formData.customer_id"
|
||||
:placeholder="t('pages.licenses.form.placeholder.selectCustomer')"
|
||||
filterable
|
||||
style="width: 100%"
|
||||
:disabled="isEdit"
|
||||
>
|
||||
<el-option
|
||||
v-for="customer in customers"
|
||||
:key="customer.id"
|
||||
:label="customer.customer_name"
|
||||
:value="customer.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="t('pages.licenses.form.description')"
|
||||
prop="description"
|
||||
>
|
||||
<el-input
|
||||
v-model="formData.description"
|
||||
:placeholder="t('pages.licenses.form.placeholder.enterDescription')"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="t('pages.licenses.form.licenseType')"
|
||||
prop="license_type"
|
||||
>
|
||||
<el-select
|
||||
v-model="formData.license_type"
|
||||
:placeholder="t('pages.licenses.form.placeholder.selectLicenseType')"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="标准版" value="standard" />
|
||||
<el-option label="专业版" value="professional" />
|
||||
<el-option label="企业版" value="enterprise" />
|
||||
<el-option label="定制版" value="custom" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="t('pages.licenses.form.expiryDate')"
|
||||
prop="expiry_date"
|
||||
>
|
||||
<el-date-picker
|
||||
v-model="formData.expiry_date"
|
||||
type="datetime"
|
||||
:placeholder="t('pages.licenses.form.placeholder.selectExpiryDate')"
|
||||
style="width: 100%"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
:disabled-date="disabledDate"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="t('pages.licenses.form.maxUsers')"
|
||||
prop="max_users"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="formData.max_users"
|
||||
:min="1"
|
||||
:max="10000"
|
||||
:placeholder="t('pages.licenses.form.placeholder.enterMaxUsers')"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3 class="section-title">高级配置</h3>
|
||||
|
||||
<el-form-item
|
||||
:label="t('pages.licenses.form.features')"
|
||||
prop="features"
|
||||
>
|
||||
<el-checkbox-group v-model="formData.features">
|
||||
<el-checkbox label="basic" value="basic">基础功能</el-checkbox>
|
||||
<el-checkbox label="advanced" value="advanced">高级功能</el-checkbox>
|
||||
<el-checkbox label="api" value="api">API接口</el-checkbox>
|
||||
<el-checkbox label="export" value="export">数据导出</el-checkbox>
|
||||
<el-checkbox label="backup" value="backup">数据备份</el-checkbox>
|
||||
<el-checkbox label="analytics" value="analytics">数据分析</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="t('pages.licenses.form.ipRestrictions')"
|
||||
prop="ip_restrictions"
|
||||
>
|
||||
<el-input
|
||||
v-model="ipRestrictionsText"
|
||||
:placeholder="t('pages.licenses.form.placeholder.enterIpRestrictions')"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
@blur="handleIpRestrictionsChange"
|
||||
/>
|
||||
<div class="form-tip">多个IP地址请用换行分隔,支持CIDR格式(如:192.168.1.0/24)</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="t('pages.licenses.form.hardwareId')"
|
||||
prop="hardware_id"
|
||||
>
|
||||
<el-input
|
||||
v-model="formData.hardware_id"
|
||||
:placeholder="t('pages.licenses.form.placeholder.enterHardwareId')"
|
||||
maxlength="100"
|
||||
/>
|
||||
<div class="form-tip">硬件ID用于绑定特定设备,留空表示不限制设备</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对话框模式 -->
|
||||
<el-dialog
|
||||
v-else
|
||||
:model-value="visible"
|
||||
:title="isEdit ? '编辑授权' : t('pages.licenses.actions.createLicense')"
|
||||
width="800px"
|
||||
@@ -158,21 +317,24 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
||||
import { createLicense, updateLicense, type License, type LicenseCreateRequest, type LicenseUpdateRequest } from '@/api/license'
|
||||
import { type Customer } from '@/api/customer'
|
||||
import { createLicense, updateLicense, type License, type AuthorizationCode, type AuthorizationCodeCreateRequest, type LicenseUpdateRequest } from '@/api/license'
|
||||
import { getCustomers, type Customer } from '@/api/customer'
|
||||
|
||||
// Props & Emits
|
||||
interface Props {
|
||||
visible: boolean
|
||||
licenseData?: License | null
|
||||
customers: Customer[]
|
||||
visible?: boolean
|
||||
licenseData?: AuthorizationCode | null
|
||||
customers?: Customer[]
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
licenseData: null
|
||||
visible: false,
|
||||
licenseData: null,
|
||||
customers: () => []
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -181,14 +343,27 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
// 判断是否为页面模式
|
||||
const isPageMode = computed(() => {
|
||||
return route.name === 'licenses-create' || route.name === 'licenses-edit'
|
||||
})
|
||||
|
||||
// Refs
|
||||
const formRef = ref<FormInstance>()
|
||||
const submitting = ref(false)
|
||||
const pageCustomers = ref<Customer[]>([])
|
||||
|
||||
// Computed
|
||||
const isEdit = computed(() => !!props.licenseData)
|
||||
|
||||
// 获取客户列表(页面模式下使用)
|
||||
const customers = computed(() => {
|
||||
return isPageMode.value ? pageCustomers.value : props.customers
|
||||
})
|
||||
|
||||
// Form Data
|
||||
const formData = reactive({
|
||||
customer_id: '',
|
||||
@@ -239,15 +414,15 @@ const resetForm = () => {
|
||||
|
||||
const loadFormData = () => {
|
||||
if (props.licenseData) {
|
||||
formData.customer_id = props.licenseData.customer_id
|
||||
formData.description = props.licenseData.description
|
||||
formData.license_type = props.licenseData.license_type
|
||||
formData.expiry_date = props.licenseData.expiry_date
|
||||
formData.max_users = props.licenseData.max_users || 1
|
||||
formData.features = [...(props.licenseData.features || [])]
|
||||
formData.ip_restrictions = [...(props.licenseData.ip_restrictions || [])]
|
||||
formData.hardware_id = props.licenseData.hardware_id || ''
|
||||
ipRestrictionsText.value = formData.ip_restrictions.join('\n')
|
||||
formData.customer_id = props.licenseData.customer_id || ''
|
||||
formData.description = props.licenseData.description || ''
|
||||
formData.license_type = ''
|
||||
formData.expiry_date = props.licenseData.end_date || ''
|
||||
formData.max_users = props.licenseData.max_activations || 1
|
||||
formData.features = []
|
||||
formData.ip_restrictions = []
|
||||
formData.hardware_id = ''
|
||||
ipRestrictionsText.value = ''
|
||||
} else {
|
||||
resetForm()
|
||||
}
|
||||
@@ -308,7 +483,7 @@ const handleSubmit = async () => {
|
||||
ElMessage.success(t('pages.licenses.message.updateSuccess'))
|
||||
} else {
|
||||
// 创建授权
|
||||
const createData: LicenseCreateRequest = {
|
||||
const createData: AuthorizationCodeCreateRequest = {
|
||||
customer_id: formData.customer_id,
|
||||
description: formData.description,
|
||||
license_type: formData.license_type,
|
||||
@@ -323,7 +498,13 @@ const handleSubmit = async () => {
|
||||
ElMessage.success(t('pages.licenses.message.createSuccess'))
|
||||
}
|
||||
|
||||
emit('success')
|
||||
if (isPageMode.value) {
|
||||
// 页面模式下跳转回主页面
|
||||
router.push({ name: 'licenses' })
|
||||
} else {
|
||||
// 对话框模式下触发成功事件
|
||||
emit('success')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to save license:', error)
|
||||
ElMessage.error(isEdit.value
|
||||
@@ -344,21 +525,97 @@ const handleClose = () => {
|
||||
}, 300)
|
||||
}
|
||||
|
||||
// 页面模式下的返回功能
|
||||
const handleBack = () => {
|
||||
router.push({ name: 'licenses' })
|
||||
}
|
||||
|
||||
// 加载客户数据(页面模式)
|
||||
const loadCustomers = async () => {
|
||||
try {
|
||||
const response = await getCustomers({ status: 'active', page_size: 1000 })
|
||||
pageCustomers.value = response.data.list
|
||||
} catch (error) {
|
||||
console.error('Failed to load customers:', error)
|
||||
ElMessage.error('加载客户列表失败')
|
||||
}
|
||||
}
|
||||
|
||||
// Watchers
|
||||
watch(() => props.visible, (visible) => {
|
||||
if (visible) {
|
||||
loadFormData()
|
||||
}
|
||||
})
|
||||
|
||||
// 页面模式下的初始化
|
||||
watch(() => route.query, (query) => {
|
||||
if (isPageMode.value && query.customerId) {
|
||||
formData.customer_id = query.customerId as string
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
if (isPageMode.value) {
|
||||
loadCustomers()
|
||||
loadFormData()
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/assets/styles/variables.scss';
|
||||
|
||||
.license-form {
|
||||
max-height: 70vh;
|
||||
// 页面模式样式
|
||||
.license-form-page {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
padding: 24px;
|
||||
border-bottom: 1px solid $border-color-light;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: $text-color-primary;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
flex: 1;
|
||||
padding: 24px;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.license-form {
|
||||
max-height: none;
|
||||
overflow-y: visible;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
<template>
|
||||
<div class="license-list-page">
|
||||
<!-- 顶部操作区 -->
|
||||
<div class="top-actions">
|
||||
<el-button type="primary" @click="handleCreate">
|
||||
创建授权
|
||||
</el-button>
|
||||
|
||||
<div class="filters">
|
||||
<span class="filter-label">筛选:</span>
|
||||
<el-select v-model="filterStatus" placeholder="状态" clearable class="filter-select">
|
||||
<el-option label="正常" value="normal" />
|
||||
<el-option label="锁定" value="locked" />
|
||||
<el-option label="过期" value="expired" />
|
||||
</el-select>
|
||||
|
||||
<el-date-picker
|
||||
v-model="filterCreateDate"
|
||||
type="date"
|
||||
placeholder="创建时间"
|
||||
clearable
|
||||
class="filter-date"
|
||||
/>
|
||||
|
||||
<el-date-picker
|
||||
v-model="filterExpiryDate"
|
||||
type="date"
|
||||
placeholder="到期时间"
|
||||
clearable
|
||||
class="filter-date"
|
||||
/>
|
||||
|
||||
<el-button type="primary" @click="handleQuery">
|
||||
查询
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<div class="table-container">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
stripe
|
||||
style="width: 100%"
|
||||
v-loading="loading"
|
||||
empty-text="暂无数据"
|
||||
>
|
||||
<el-table-column prop="code" label="授权码" width="200" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getStatusTagType(row.status)">
|
||||
{{ getStatusText(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="current_activations" label="激活状态" width="120" />
|
||||
<el-table-column prop="end_date" label="到期时间" width="180" />
|
||||
<el-table-column prop="description" label="备注" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" size="small" @click="handleDetail(row)">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.is_locked"
|
||||
type="success"
|
||||
size="small"
|
||||
@click="handleUnlock(row)"
|
||||
>
|
||||
解锁
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
type="warning"
|
||||
size="small"
|
||||
@click="handleLock(row)"
|
||||
>
|
||||
锁定
|
||||
</el-button>
|
||||
<el-button type="danger" size="small" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
:current-page="currentPage"
|
||||
:page-size="pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { getLicenses, lockAuthorizationCode, type AuthorizationCode, type LicenseQueryRequest } from '@/api/license'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
// 从路由参数获取客户信息
|
||||
const customerInfo = computed(() => {
|
||||
return {
|
||||
id: route.query.customerId as string || '',
|
||||
name: route.query.customerName as string || ''
|
||||
}
|
||||
})
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
const filterStatus = ref('')
|
||||
const filterCreateDate = ref('')
|
||||
const filterExpiryDate = ref('')
|
||||
const tableData = ref<AuthorizationCode[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
|
||||
// 方法
|
||||
const getStatusTagType = (status: string) => {
|
||||
switch (status) {
|
||||
case 'normal':
|
||||
return 'success'
|
||||
case 'locked':
|
||||
return 'warning'
|
||||
case 'expired':
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
}
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case 'normal':
|
||||
return '正常'
|
||||
case 'locked':
|
||||
return '锁定'
|
||||
case 'expired':
|
||||
return '过期'
|
||||
default:
|
||||
return status
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreate = () => {
|
||||
router.push({
|
||||
name: 'licenses-create',
|
||||
query: {
|
||||
customerId: customerInfo.value.id,
|
||||
customerName: customerInfo.value.name
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
console.log('点击查询按钮')
|
||||
loadData()
|
||||
}
|
||||
|
||||
const handleDetail = (_row: any) => {
|
||||
ElMessage.info('查看详情功能待实现')
|
||||
}
|
||||
|
||||
const handleLock = async (row: any) => {
|
||||
ElMessageBox.confirm('确定要锁定此授权吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
await lockAuthorizationCode(row.id, {
|
||||
is_locked: true,
|
||||
lock_reason: '手动锁定',
|
||||
reason: '管理员手动锁定'
|
||||
})
|
||||
ElMessage.success('锁定成功')
|
||||
loadData()
|
||||
} catch (error) {
|
||||
console.error('锁定失败:', error)
|
||||
ElMessage.error('锁定失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
// 取消操作
|
||||
})
|
||||
}
|
||||
|
||||
const handleUnlock = async (row: any) => {
|
||||
ElMessageBox.confirm('确定要解锁此授权吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
await lockAuthorizationCode(row.id, {
|
||||
is_locked: false,
|
||||
lock_reason: '手动解锁',
|
||||
reason: '管理员手动解锁'
|
||||
})
|
||||
ElMessage.success('解锁成功')
|
||||
loadData()
|
||||
} catch (error) {
|
||||
console.error('解锁失败:', error)
|
||||
ElMessage.error('解锁失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
// 取消操作
|
||||
})
|
||||
}
|
||||
|
||||
const handleDelete = (_row: any) => {
|
||||
ElMessageBox.confirm('确定要删除此授权吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
ElMessage.success('删除成功')
|
||||
loadData()
|
||||
}).catch(() => {
|
||||
// 取消操作
|
||||
})
|
||||
}
|
||||
|
||||
const handleSizeChange = (size: number) => {
|
||||
pageSize.value = size
|
||||
loadData()
|
||||
}
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
currentPage.value = page
|
||||
loadData()
|
||||
}
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const queryParams: LicenseQueryRequest = {
|
||||
page: currentPage.value,
|
||||
page_size: pageSize.value,
|
||||
customer_id: customerInfo.value.id || '',
|
||||
sort: 'created_at',
|
||||
order: 'desc'
|
||||
}
|
||||
|
||||
// 添加状态筛选
|
||||
if (filterStatus.value) {
|
||||
queryParams.status = filterStatus.value as 'normal' | 'locked' | 'expired'
|
||||
}
|
||||
|
||||
console.log('查询参数:', queryParams)
|
||||
const response = await getLicenses(queryParams)
|
||||
console.log('API响应:', response)
|
||||
tableData.value = response.data.list
|
||||
total.value = response.data.total
|
||||
console.log('表格数据:', tableData.value)
|
||||
} catch (error) {
|
||||
console.error('加载数据失败:', error)
|
||||
ElMessage.error('加载数据失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
console.log('LinenseList组件已挂载')
|
||||
console.log('客户信息:', customerInfo.value)
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.license-list-page {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
min-height: calc(100vh - 80px);
|
||||
}
|
||||
|
||||
.customer-info {
|
||||
margin-bottom: 20px;
|
||||
padding: 16px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #409eff;
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-info {
|
||||
margin-bottom: 20px;
|
||||
padding: 16px;
|
||||
background: #fff3cd;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #ffc107;
|
||||
|
||||
p {
|
||||
margin: 4px 0;
|
||||
font-size: 14px;
|
||||
color: #856404;
|
||||
}
|
||||
}
|
||||
|
||||
.top-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.filter-date {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 768px) {
|
||||
.license-list-page {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.top-actions {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filters {
|
||||
width: 100%;
|
||||
|
||||
.filter-select,
|
||||
.filter-date {
|
||||
flex: 1;
|
||||
min-width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user