Commit 96b0e396 by renyizhao

算力资源改造完成

parent 21fe1bfc
...@@ -54,11 +54,29 @@ export function delOrder (id) { ...@@ -54,11 +54,29 @@ export function delOrder (id) {
} }
// 取消订单-订单管理 // 取消订单-订单管理
export function cancelOrder (data) { // 阶段 F:旧的 /api/v1/orderCancel 端点(ApiControllerUnuse)整段被注释,调它会 404
// 改为调新的 /compute/order/cancel(AppResourceOrderController 已实现)
export function cancelOrder (data){
return request({ return request({
url: '/api/v1/orderCancel', url: '/compute/order/cancel',
method: 'post', method: 'put',
data: data params: data
})
}
/**
* 阶段 F:获取订单发货信息(IP / 用户名 / 密码)
* 独立的接口,避免在主订单接口下发敏感字段。
* 后端:AppResourceOrderController.getUserResourceOrderDeliveryInfo
* 响应:AppResourceOrderDeliveryInfoRespVO
* - 非 DELIVERED 状态:发货字段为 null
* - 订单必须属于当前用户
*/
export function getOrderDeliveryInfo (orderId) {
return request({
url: '/compute/order/get-delivery-info',
method: 'get',
params: { id: orderId }
}) })
} }
......
...@@ -3,7 +3,10 @@ import { getDicts } from '@/api/system/dict/data' ...@@ -3,7 +3,10 @@ import { getDicts } from '@/api/system/dict/data'
import request from "@/utils/request.js"; import request from "@/utils/request.js";
export const DICT_TYPE = { export const DICT_TYPE = {
TRADE_INVOICE_STATUS: 'trade_invoice_status' TRADE_INVOICE_STATUS: 'trade_invoice_status',
// ========== 阶段 F 客户端字典 ==========
COMPUTE_RESOURCE_ORDER_STATUS: 'compute_resource_order_status',
COMPUTE_RESOURCE_REFUND_STATUS: 'compute_resource_refund_status'
} }
/** /**
......
<template> <template>
<div class="app-container order-result"> <div class="app-container order-result">
<img src="@/assets/images/order-success.png" alt=""> <img src="@/assets/images/order-success.png" alt="">
<div class="text">申请成功</div> <!-- 阶段 F:支付成功 → 商家备货中(不再"立即生效",因为阶段 A 后发货才计费) -->
<div class="sub-text">如有需要可返回产品页继续选购</div> <div class="text">申请成功,商家备货中</div>
<div class="sub-text">支付完成后商家将为您准备资源,预计 24 小时内完成发货并通过短信通知您</div>
<!-- 规格展示(仅当路由 state 传入 specMap 时显示) --> <!-- 规格展示(仅当路由 state 传入 specMap 时显示) -->
<div v-if="specEntries.length > 0" class="spec-block"> <div v-if="specEntries.length > 0" class="spec-block">
...@@ -14,7 +15,7 @@ ...@@ -14,7 +15,7 @@
</div> </div>
<div> <div>
<el-button type="primary" @click="$router.replace('/computingResource/resourceList')">继续选购</el-button> <el-button type="primary" @click="$router.replace('/console/myOrder')">查看我的订单</el-button>
</div> </div>
<div> <div>
<el-button type="default" @click="$router.replace('/')">返回首页</el-button> <el-button type="default" @click="$router.replace('/')">返回首页</el-button>
......
...@@ -7,14 +7,6 @@ ...@@ -7,14 +7,6 @@
v-show="showSearch" v-show="showSearch"
label-width="68px" label-width="68px"
> >
<!-- <el-form-item label="订单编号" prop="orderNo">-->
<!-- <el-input-->
<!-- v-model="queryParams.orderNo"-->
<!-- placeholder="请输入订单编号"-->
<!-- clearable-->
<!-- @keyup.enter="handleQuery"-->
<!-- />-->
<!-- </el-form-item>-->
<el-form-item label="资源类型" prop="category"> <el-form-item label="资源类型" prop="category">
<el-select <el-select
v-model="queryParams.categoryId" v-model="queryParams.categoryId"
...@@ -31,62 +23,6 @@ ...@@ -31,62 +23,6 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- <el-form-item label="型号" prop="model">-->
<!-- <el-select-->
<!-- v-model="queryParams.model"-->
<!-- placeholder="请选择型号"-->
<!-- clearable-->
<!-- >-->
<!-- <el-option-->
<!-- v-for="dict in model"-->
<!-- :key="dict.value"-->
<!-- :label="dict.label"-->
<!-- :value="dict.value"-->
<!-- />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="CPU" prop="cpu">-->
<!-- <el-select v-model="queryParams.cpu" placeholder="请选择CPU" clearable>-->
<!-- <el-option-->
<!-- v-for="dict in cpu_type"-->
<!-- :key="dict.value"-->
<!-- :label="dict.label"-->
<!-- :value="dict.value"-->
<!-- />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="GPU" prop="gpu">-->
<!-- <el-select v-model="queryParams.gpu" placeholder="请选择GPU" clearable>-->
<!-- <el-option-->
<!-- v-for="dict in gpu_type"-->
<!-- :key="dict.value"-->
<!-- :label="dict.label"-->
<!-- :value="dict.value"-->
<!-- />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="内存" prop="memory">-->
<!-- <el-select-->
<!-- v-model="queryParams.memory"-->
<!-- placeholder="请选择内存"-->
<!-- clearable-->
<!-- >-->
<!-- <el-option-->
<!-- v-for="dict in memory_type"-->
<!-- :key="dict.value"-->
<!-- :label="dict.label"-->
<!-- :value="dict.value"-->
<!-- />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="算力金额" prop="price">-->
<!-- <el-input-->
<!-- v-model="queryParams.price"-->
<!-- placeholder="请输入算力金额"-->
<!-- clearable-->
<!-- @keyup.enter="handleQuery"-->
<!-- />-->
<!-- </el-form-item>-->
<el-form-item> <el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery" <el-button type="primary" icon="Search" @click="handleQuery"
>搜索 >搜索
...@@ -101,11 +37,8 @@ ...@@ -101,11 +37,8 @@
:data="orderList" :data="orderList"
:max-height="620" :max-height="620"
> >
<!--<el-table-column type="selection" width="45" align="center"/>-->
<!--<el-table-column label="序号" align="center" prop="id"/>-->
<el-table-column label="商品名称" align="center" prop="spuName"/> <el-table-column label="商品名称" align="center" prop="spuName"/>
<el-table-column label="订单编号" align="center" prop="orderNo"/> <el-table-column label="订单编号" align="center" prop="orderNo"/>
<!--<el-table-column label="商品数量" align="center" prop="productCount"/>-->
<el-table-column label="费用" align="center" prop="paymentPrice"> <el-table-column label="费用" align="center" prop="paymentPrice">
<template #default="scope"> <template #default="scope">
{{ scope.row.paymentPrice / 100 }} {{ scope.row.paymentPrice / 100 }}
...@@ -123,12 +56,11 @@ ...@@ -123,12 +56,11 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="所在地" align="center" prop="location"/> <el-table-column label="所在地" align="center" prop="location"/>
<el-table-column label="订单状态" align="center" prop="status"> <el-table-column label="订单状态" align="center" prop="status" width="100">
<template #default="scope"> <template #default="scope">
<dict-tag :options="compute_resource_order_status" :value="scope.row.status" /> <dict-tag :options="compute_resource_order_status" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<!-- <el-table-column label="发票链接" align="center" prop="invoiceUrl"/>-->
<el-table-column label="开票状态" align="center" prop="invoiceStatus"> <el-table-column label="开票状态" align="center" prop="invoiceStatus">
<template #default="scope"> <template #default="scope">
<dict-tag :options="trade_invoice_status" :value="scope.row.invoiceStatus" /> <dict-tag :options="trade_invoice_status" :value="scope.row.invoiceStatus" />
...@@ -143,10 +75,25 @@ ...@@ -143,10 +75,25 @@
label="操作" label="操作"
align="center" align="center"
fixed="right" fixed="right"
min-width="280"
class-name="small-padding fixed-width"> class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<!-- 阶段 F:详情按钮(所有状态可见) -->
<el-button link type="primary" @click="handleDetails(scope.row)">详情</el-button>
<!-- 阶段 F:取消订单 / 申请退款(仅 UNPAID=0 PENDING_DELIVERY=5 可取消) -->
<el-button <el-button
v-if="scope.row.invoiceStatus === InvoiceRequestEnum.UNINVOICED.type" v-if="scope.row.status === 0 || scope.row.status === 5"
link
type="warning"
@click="handleCancelOrder(scope.row)"
>
{{ scope.row.status === 5 ? '申请退款' : '取消订单' }}
</el-button>
<!-- 阶段 F:申请开票条件改为 status===6DELIVERED+ 仍未开票 -->
<el-button
v-if="scope.row.status === 6 && scope.row.invoiceStatus === InvoiceRequestEnum.UNINVOICED.type"
link link
type="primary" type="primary"
@click="invoiceRequest(scope.row)" @click="invoiceRequest(scope.row)"
...@@ -167,6 +114,16 @@ ...@@ -167,6 +114,16 @@
</el-table> </el-table>
<!-- 阶段 F:列表底部 PENDING_REFUND 状态统一提示(可改为按行展示) -->
<div v-if="hasPendingRefund" class="refund-banner">
<el-alert
:title="`您有 ${pendingRefundCount} 个订单处于「待退款」状态,款项将由客服人工处理(1-3 个工作日)`"
type="warning"
:closable="false"
show-icon
/>
</div>
<pagination <pagination
v-show="total > 0" v-show="total > 0"
:total="total" :total="total"
...@@ -191,27 +148,76 @@ ...@@ -191,27 +148,76 @@
<!-- 添加或修改订单管理-需求单管理对话框 --> <!-- 添加或修改订单管理-需求单管理对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body> <el-dialog :title="title" v-model="open" width="640px" append-to-body>
<div> <div v-loading="detailLoading">
<div class="info-block"> <!-- 阶段 F:状态卡片顶部 el-alert 提示(按 status 分支) -->
<div class="info-item flex-align-center flex-space-between"> <el-alert
<div class="label">商品名称</div> v-if="form.status === 0"
<div class="value">{{ form.spuName }}</div> type="info"
</div> :closable="false"
show-icon
title="订单待支付,请尽快完成支付"
/>
<el-alert
v-else-if="form.status === 5"
type="warning"
:closable="false"
show-icon
title="商家备货中,预计 24 小时内发货"
/>
<el-alert
v-else-if="form.status === 6"
type="success"
:closable="false"
show-icon
:title="`资源使用中,租赁结束时间:${parseTime(form.rentEndTime, '{y}-{m}-{d} {h}:{i}:{s}')}`"
/>
<el-alert
v-else-if="form.status === 7"
type="warning"
:closable="false"
show-icon
title="订单已取消,正在等待客服处理退款(1-3 个工作日)"
/>
<el-alert
v-else-if="form.status === 4"
type="success"
:closable="false"
show-icon
title="订单已退款完成"
/>
<el-alert
v-else-if="form.status === 2"
type="info"
:closable="false"
show-icon
title="订单已结束"
/>
<el-alert
v-else-if="form.status === 3"
type="info"
:closable="false"
show-icon
title="订单已取消"
/>
<div class="info-block" style="margin-top: 12px;">
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">订单编号</div> <div class="label">订单编号</div>
<div class="value">{{ form.orderNo }}</div> <div class="value">{{ form.orderNo }}</div>
</div> </div>
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">商品名称</div>
<div class="value">{{ form.spuName }}</div>
</div>
<div class="info-item flex-align-center flex-space-between">
<div class="label">费用</div> <div class="label">费用</div>
<div class="value">{{ form.paymentPrice ? (form.paymentPrice / 100) : '-' }}</div> <div class="value">{{ form.paymentPrice ? (form.paymentPrice / 100) : '-' }}</div>
</div> </div>
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">资源类型</div> <div class="label">资源类型</div>
<div class="value">{{ form.categoryName }}</div> <div class="value">{{ form.categoryName }}</div>
</div> </div>
</div> </div>
<div class="info-block"> <div class="info-block">
...@@ -224,18 +230,49 @@ ...@@ -224,18 +230,49 @@
<div class="value">-</div> <div class="value">-</div>
</div> </div>
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">服务器IP</div>
<div class="value">{{ form.ip || '-' }}</div>
</div>
<div class="info-item flex-align-center flex-space-between">
<div class="label">服务器所在地</div> <div class="label">服务器所在地</div>
<div class="value">{{ form.location || '-' }}</div> <div class="value">{{ form.location || '-' }}</div>
</div> </div>
</div> </div>
<!-- 阶段 F:发货信息区(仅 DELIVERED 状态显示),字段从 getDeliveryInfo -->
<div v-if="form.status === 6" class="info-block">
<div class="info-block-title">发货信息</div>
<div class="info-item flex-align-center flex-space-between">
<div class="label">服务器 IP</div>
<div class="value">{{ form.deliveryIp || '-' }}</div>
</div>
<div class="info-item flex-align-center flex-space-between">
<div class="label">初始用户名</div>
<div class="value">{{ form.deliveryUsername || '-' }}</div>
</div>
<div class="info-item flex-align-center flex-space-between">
<div class="label">初始密码</div>
<div class="value">
<span v-if="showPassword">{{ form.deliveryPassword || '-' }}</span>
<el-button v-else link type="primary" @click="showPassword = true">显示密码</el-button>
</div>
</div>
<div class="info-item flex-align-center flex-space-between">
<div class="label">发货时间</div>
<div class="value">{{ parseTime(form.deliveredTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</div>
</div>
</div>
<!-- 阶段 F:退款信息(仅 PENDING_REFUND=7 REFUNDED=4 -->
<div v-if="form.status === 7 || form.status === 4" class="info-block">
<div class="info-block-title">退款信息</div>
<div class="info-item flex-align-center flex-space-between">
<div class="label">退款时间</div>
<div class="value">{{ parseTime(form.refundTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</div>
</div>
</div>
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">订单状态</div> <div class="label">订单状态</div>
<div class="value">{{ form.statusName }}</div> <div class="value">
<dict-tag :options="compute_resource_order_status" :value="form.status" />
</div>
</div> </div>
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">下单时间</div> <div class="label">下单时间</div>
...@@ -244,6 +281,14 @@ ...@@ -244,6 +281,14 @@
</div> </div>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<!-- 阶段 F:详情页底部操作按钮 -->
<el-button
v-if="form.status === 0 || form.status === 5"
type="warning"
@click="handleCancelOrder(form)"
>
{{ form.status === 5 ? '申请退款' : '取消订单' }}
</el-button>
<el-button @click="cancel">关闭</el-button> <el-button @click="cancel">关闭</el-button>
</div> </div>
</template> </template>
...@@ -254,23 +299,26 @@ ...@@ -254,23 +299,26 @@
<script setup name="MyOrder"> <script setup name="MyOrder">
import { import {
listOrder listOrder,
cancelOrder,
orderInvoiceRequest,
getCategories,
getOrderDeliveryInfo
} from '@/api/computility/order' } from '@/api/computility/order'
import {orderInvoiceRequest, getCategories} from '@/api/computility/order'
import {parseTime} from "../../utils/ruoyi.js"; import {parseTime} from "../../utils/ruoyi.js";
import {getResources} from "@/api/computility/resources.js"; import {getResources} from "@/api/computility/resources.js";
import {ElMessage} from "element-plus";
import {InvoiceRequestEnum} from "@/utils/constants.js"; import {InvoiceRequestEnum} from "@/utils/constants.js";
const {proxy} = getCurrentInstance() const {proxy} = getCurrentInstance()
const {trade_invoice_status, compute_resource_order_status} = proxy.useDict('trade_invoice_status', 'compute_resource_order_status') const {trade_invoice_status, compute_resource_order_status} = proxy.useDict('trade_invoice_status', 'compute_resource_order_status')
const orderList = ref([]) const orderList = ref([])
const open = ref(false) const open = ref(false)
const sOpen = ref(false)
const loading = ref(true) const loading = ref(true)
const detailLoading = ref(false)
const showSearch = ref(true) const showSearch = ref(true)
const total = ref(0) const total = ref(0)
const title = ref('') const title = ref('')
const showPassword = ref(false) // 阶段 F:详情页密码默认隐藏
const data = reactive({ const data = reactive({
form: {}, form: {},
...@@ -278,50 +326,15 @@ const data = reactive({ ...@@ -278,50 +326,15 @@ const data = reactive({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
orderNo: null, orderNo: null,
categoryId: null, categoryId: null
model: null,
cpu: null,
gpu: null,
memory: null,
storage: null,
price: null,
orderStatus: null,
networkId: null,
ipNum: null,
vmStorage: null,
submitTime: null,
applyUser: null,
useTime: null
},
rules: {
category: [
{required: true, message: '商品类别不能为空', trigger: 'change'}
],
model: [{required: true, message: '型号不能为空', trigger: 'change'}],
cpu: [{required: true, message: 'CPU不能为空', trigger: 'change'}],
gpu: [{required: true, message: 'GPU不能为空', trigger: 'change'}],
memory: [{required: true, message: '内存不能为空', trigger: 'change'}],
storage: [
{required: true, message: '存储硬盘不能为空', trigger: 'change'}
],
orderStatus: [
{required: true, message: '状态不能为空', trigger: 'change'}
],
networkId: [
{required: true, message: '网络带宽不能为空', trigger: 'blur'}
],
submitTime: [
{required: true, message: '下单时间不能为空', trigger: 'blur'}
],
applyUser: [{required: true, message: '应用人不能为空', trigger: 'blur'}],
useTime: [{required: true, message: '使用期限不能为空', trigger: 'blur'}],
createTime: [
{required: true, message: '创建时间不能为空', trigger: 'blur'}
]
} }
}) })
const {queryParams, form, rules} = toRefs(data) const {queryParams, form} = toRefs(data)
/** 阶段 F:列表中待退款订单数(底部统一提示) */
const pendingRefundCount = computed(() => orderList.value.filter(o => o.status === 7).length)
const hasPendingRefund = computed(() => pendingRefundCount.value > 0)
/** 查询订单管理-需求单管理列表 */ /** 查询订单管理-需求单管理列表 */
function getList() { function getList() {
...@@ -350,30 +363,8 @@ function cancel() { ...@@ -350,30 +363,8 @@ function cancel() {
// 表单重置 // 表单重置
function reset() { function reset() {
form.value = { form.value = {}
id: null, showPassword.value = false
orderNo: null,
categoryId: null,
model: null,
cpu: null,
gpu: null,
memory: null,
storage: null,
price: null,
orderStatus: null,
networkId: null,
ipNum: null,
vmStorage: null,
submitTime: null,
applyUser: null,
useTime: null,
createTime: null,
createBy: null,
updateBy: null,
updateTime: null,
remark: null,
delFlag: null
}
proxy.resetForm('orderRef') proxy.resetForm('orderRef')
} }
...@@ -389,13 +380,36 @@ function resetQuery() { ...@@ -389,13 +380,36 @@ function resetQuery() {
handleQuery() handleQuery()
} }
function handleDetails(row) { /**
const _id = row.id || ids.value * 阶段 F:实装详情弹窗
// getResources({id: _id}).then(response => { * 1. 调 /compute/order/get 拿主订单数据
// form.value = response.data * 2. 调 /compute/order/get-delivery-info 拿发货字段(仅 DELIVERED 状态有值)
*/
async function handleDetails(row) {
open.value = true open.value = true
title.value = '查看订单' title.value = '查看订单'
// }) detailLoading.value = true
showPassword.value = false
try {
// 主订单
const mainRes = await getResources({ id: row.id })
form.value = mainRes.data || {}
// 阶段 F:独立拿发货信息(IP/用户名/密码)
try {
const delRes = await getOrderDeliveryInfo(row.id)
const info = delRes.data || {}
form.value.deliveryIp = info.deliveryIp
form.value.deliveryUsername = info.deliveryUsername
form.value.deliveryPassword = info.deliveryPassword
form.value.deliveredTime = info.deliveredTime
form.value.deliverOperatorName = info.deliverOperatorName
} catch (e) {
// 发货信息拿不到不阻塞主流程(订单可能非本人或非 DELIVERED)
console.warn('[getDeliveryInfo] failed', e)
}
} finally {
detailLoading.value = false
}
} }
function invoiceRequest(row) { function invoiceRequest(row) {
...@@ -407,6 +421,38 @@ function invoiceRequest(row) { ...@@ -407,6 +421,38 @@ function invoiceRequest(row) {
) )
} }
/**
* 阶段 F:取消订单 / 申请退款
* - status=0(UNPAID) → 显示"取消订单",文案"确认取消该订单?"
* - status=5(PENDING_DELIVERY) → 显示"申请退款",文案"取消后商家将停止备货,款项将由客服人工退款(1-3 个工作日)"
* 调 PUT /compute/order/cancel?orderId=xxx
*/
async function handleCancelOrder(row) {
if (!row || !row.id) return
const isPendingDelivery = row.status === 5
const confirmText = isPendingDelivery
? '确认取消该订单?取消后商家将停止备货,款项将由客服人工退款(1-3 个工作日)'
: '确认取消该订单?'
try {
// 阶段 F:使用 proxy.$modal.confirm 与项目风格保持一致
await proxy.$modal.confirm(confirmText, isPendingDelivery ? '申请退款' : '取消订单')
} catch {
return
}
try {
await cancelOrder({ orderId: row.id })
proxy.$modal.msgSuccess(isPendingDelivery ? '已取消,等待客服退款' : '已取消')
// 刷新列表
getList()
// 如果详情弹窗打开着,刷新详情
if (open.value && form.value && form.value.id === row.id) {
await handleDetails(row)
}
} catch (e) {
console.error('[cancelOrder] failed', e)
}
}
const pdfDialogVisible = ref(false) const pdfDialogVisible = ref(false)
const invoiceUrl = ref('') const invoiceUrl = ref('')
...@@ -437,7 +483,18 @@ loadCategories() ...@@ -437,7 +483,18 @@ loadCategories()
.info-block { .info-block {
background-color: #ffffff; background-color: #ffffff;
padding: 4px 0; padding: 4px 0;
//margin-bottom: 14px; }
.info-block-title {
padding: 8px 20px 4px;
font-weight: 600;
font-size: 14px;
color: #303233;
background-color: #F7F8F9;
}
.refund-banner {
margin: 12px 0;
} }
.info-item { .info-item {
......
...@@ -18,11 +18,20 @@ ...@@ -18,11 +18,20 @@
<span>{{ scope.row.skuName }}</span> <span>{{ scope.row.skuName }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="订单状态" align="center" prop="status"> <el-table-column label="订单状态" align="center" prop="status" width="100">
<template #default="scope"> <template #default="scope">
<dict-tag :options="compute_resource_order_status" :value="scope.row.status" /> <dict-tag :options="compute_resource_order_status" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<!-- 阶段 F:倒计时(仅 DELIVERED 显示),剩余 ≤3 天橙色警示 -->
<el-table-column label="剩余时间" align="center" width="140">
<template #default="scope">
<span v-if="scope.row.status === 6" :class="{ 'warn-text': remainingDays(scope.row) <= 3 && remainingDays(scope.row) >= 0 }">
剩余 {{ formatRemaining(scope.row.rentEndTime) }}
</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="申请时间" align="center" prop="createTime" width="180"> <el-table-column label="申请时间" align="center" prop="createTime" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span> <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
...@@ -35,7 +44,7 @@ ...@@ -35,7 +44,7 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row,scope.$index)">详情</el-button> <el-button link type="primary" @click="handleUpdate(scope.row)">详情</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
...@@ -50,8 +59,45 @@ ...@@ -50,8 +59,45 @@
<!-- 添加或修改订单管理-用户资源管理对话框 --> <!-- 添加或修改订单管理-用户资源管理对话框 -->
<el-dialog :title="title" v-model="open" width="800px" append-to-body> <el-dialog :title="title" v-model="open" width="800px" append-to-body>
<div> <div v-loading="detailLoading">
<div class="info-block"> <!-- 阶段 F:状态顶部 el-alert 提示(按 status 分支) -->
<el-alert
v-if="form.status === 6"
type="success"
:closable="false"
show-icon
:title="`资源使用中,租赁结束时间:${parseTime(form.rentEndTime, '{y}-{m}-{d}')}`"
/>
<el-alert
v-else-if="form.status === 4"
type="info"
:closable="false"
show-icon
title="已退款完成"
/>
<el-alert
v-else-if="form.status === 2"
type="info"
:closable="false"
show-icon
title="订单已结束"
/>
<el-alert
v-else-if="form.status === 5"
type="warning"
:closable="false"
show-icon
title="商家备货中,预计 24 小时内发货"
/>
<el-alert
v-else-if="form.status === 7"
type="warning"
:closable="false"
show-icon
title="订单已取消,正在等待客服处理退款"
/>
<div class="info-block" style="margin-top: 12px;">
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">订单编号</div> <div class="label">订单编号</div>
<div class="value">{{ form.orderNo }}</div> <div class="value">{{ form.orderNo }}</div>
...@@ -71,22 +117,46 @@ ...@@ -71,22 +117,46 @@
<div class="value">-</div> <div class="value">-</div>
</div> </div>
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">服务器IP</div>
<div class="value">{{ form.ip || '-' }}</div>
</div>
<div class="info-item flex-align-center flex-space-between">
<div class="label">服务器所在地</div> <div class="label">服务器所在地</div>
<div class="value">{{ form.location || '-' }}</div> <div class="value">{{ form.location || '-' }}</div>
</div> </div>
</div>
<!-- 阶段 F:发货信息区(仅 DELIVERED 状态显示),字段从 getDeliveryInfo -->
<div v-if="form.status === 6" class="info-block">
<div class="info-block-title">发货信息</div>
<div class="info-item flex-align-center flex-space-between">
<div class="label">服务器 IP</div>
<div class="value">{{ form.deliveryIp || '-' }}</div>
</div>
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">服务器用户名</div> <div class="label">服务器用户名</div>
<div class="value">{{ form.initUsername || '-' }}</div> <div class="value">{{ form.deliveryUsername || '-' }}</div>
</div> </div>
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">服务器密码</div> <div class="label">服务器密码</div>
<div class="value">{{ form.initPassword || '-' }}</div> <div class="value">
<span v-if="showPassword">{{ form.deliveryPassword || '-' }}</span>
<el-button v-else link type="primary" @click="showPassword = true">显示密码</el-button>
</div>
</div> </div>
<div class="info-item flex-align-center flex-space-between"> <div class="info-item flex-align-center flex-space-between">
<div class="label">发货时间</div>
<div class="value">{{ parseTime(form.deliveredTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</div>
</div>
</div>
<!-- 阶段 F:退款信息(仅 PENDING_REFUND=7 REFUNDED=4 -->
<div v-if="form.status === 7 || form.status === 4" class="info-block">
<div class="info-block-title">退款信息</div>
<div class="info-item flex-align-center flex-space-between">
<div class="label">退款时间</div>
<div class="value">{{ parseTime(form.refundTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</div>
</div>
</div>
<div class="info-block">
<div class="info-item flex-align-center flex-space-between">
<div class="label">租期</div> <div class="label">租期</div>
<div class="value">{{ form.skuName }}</div> <div class="value">{{ form.skuName }}</div>
</div> </div>
...@@ -122,6 +192,7 @@ ...@@ -122,6 +192,7 @@
<script setup name="NaturalResources"> <script setup name="NaturalResources">
import {listResources, getResources} from '@/api/computility/resources' import {listResources, getResources} from '@/api/computility/resources'
import {getOrderDeliveryInfo} from '@/api/computility/order'
import {parseTime} from "../../utils/ruoyi.js"; import {parseTime} from "../../utils/ruoyi.js";
const {proxy} = getCurrentInstance() const {proxy} = getCurrentInstance()
...@@ -130,9 +201,11 @@ const {compute_resource_order_status} = proxy.useDict('compute_resource_order_st ...@@ -130,9 +201,11 @@ const {compute_resource_order_status} = proxy.useDict('compute_resource_order_st
const resourcesList = ref([]) const resourcesList = ref([])
const open = ref(false) const open = ref(false)
const loading = ref(true) const loading = ref(true)
const detailLoading = ref(false)
const ids = ref([]) const ids = ref([])
const total = ref(0) const total = ref(0)
const title = ref('') const title = ref('')
const showPassword = ref(false) // 阶段 F:密码默认隐藏
const data = reactive({ const data = reactive({
form: {}, form: {},
...@@ -159,14 +232,62 @@ function cancel() { ...@@ -159,14 +232,62 @@ function cancel() {
open.value = false open.value = false
} }
/** 修改按钮操作 */ /**
function handleUpdate(row) { * 阶段 F:计算剩余天数(仅 DELIVERED 用到)
const _id = row.id || ids.value * @returns 剩余天数(已过期返回负数;无 rentEndTime 返回 NaN)
getResources({id: _id}).then(response => { */
form.value = response.data function remainingDays(row) {
if (!row || !row.rentEndTime) return NaN
const end = new Date(row.rentEndTime).getTime()
if (isNaN(end)) return NaN
const now = Date.now()
return Math.floor((end - now) / (1000 * 60 * 60 * 24))
}
/**
* 阶段 F:格式化剩余时间为 "X 天 Y 小时"
*/
function formatRemaining(endTime) {
if (!endTime) return '-'
const end = new Date(endTime).getTime()
if (isNaN(end)) return '-'
const diffMs = end - Date.now()
if (diffMs <= 0) return '已到期'
const totalHours = Math.floor(diffMs / (1000 * 60 * 60))
const days = Math.floor(totalHours / 24)
const hours = totalHours % 24
if (days > 0) return `${days} 天 ${hours} 小时`
return `${hours} 小时`
}
/**
* 阶段 F:实装详情弹窗
* 1. 调 /compute/order/get 拿主订单数据
* 2. 调 /compute/order/get-delivery-info 拿发货字段(仅 DELIVERED 状态有值)
*/
async function handleUpdate(row) {
open.value = true open.value = true
title.value = '查看详情' title.value = '查看详情'
}) showPassword.value = false
detailLoading.value = true
try {
const mainRes = await getResources({ id: row.id })
form.value = mainRes.data || {}
// 阶段 F:独立拿发货信息(IP/用户名/密码)
try {
const delRes = await getOrderDeliveryInfo(row.id)
const info = delRes.data || {}
form.value.deliveryIp = info.deliveryIp
form.value.deliveryUsername = info.deliveryUsername
form.value.deliveryPassword = info.deliveryPassword
form.value.deliveredTime = info.deliveredTime
form.value.deliverOperatorName = info.deliverOperatorName
} catch (e) {
console.warn('[getDeliveryInfo] failed', e)
}
} finally {
detailLoading.value = false
}
} }
getList() getList()
...@@ -191,7 +312,14 @@ getList() ...@@ -191,7 +312,14 @@ getList()
.info-block { .info-block {
background-color: #ffffff; background-color: #ffffff;
padding: 4px 0; padding: 4px 0;
//margin-bottom: 14px; }
.info-block-title {
padding: 8px 20px 4px;
font-weight: 600;
font-size: 14px;
color: #303233;
background-color: #F7F8F9;
} }
.info-item { .info-item {
...@@ -225,4 +353,10 @@ getList() ...@@ -225,4 +353,10 @@ getList()
color: #303233; color: #303233;
font-weight: 500; font-weight: 500;
} }
/* 阶段 F:剩余时间 ≤ 3 天橙色警示 */
.warn-text {
color: #E6A23C;
font-weight: 600;
}
</style> </style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment