feat: C2C商家交易系统 — 商家列表+下单+我的订单

This commit is contained in:
testadmin
2026-04-28 14:12:00 +08:00
commit 1d17732bfc
9 changed files with 4399 additions and 0 deletions
+842
View File
@@ -0,0 +1,842 @@
import Vue from 'vue'
import Router from 'vue-router'
import home from '@/components/home'
import homeContent from '@/view/homeContent'
import dealCenter from '@/components/dealCenterV2'
import dealCenterOld from '@/components/dealCenter'
import fiatradCenter from '@/components/fiatradCenter'
import payOpts from '@/view/payOpts'
import userSetting from '@/components/userSetting'
import c2c from '@/components/c2c'
import c2cOrder from '@/view/c2c/c2cOrder'
import c2cRelease from '@/view/c2c/c2cRelease'
import c2cTrade from '@/view/c2c/c2cTrade'
import c2cDetail from '@/view/c2c/c2cDetail'
import leverdealCenter from '@/components/lever_dealCenter'
import leverdealCenterV2 from '@/components/leverDealCenterV2'
import login from '@/components/login'
import register from '@/components/register'
import account from '@/components/account'
import accountCenter from '@/view/accountCenter'
import accountEntrust from '@/view/accountEntrust'
import accountSet from '@/view/accountSet'
import authentication from '@/view/authentication'
import advanced from '@/view/advanced'
import payment from '@/view/payment'
import accountMessage from '@/view/accountMessage'
import setCash from '@/components/setCash'
import rechargeMoney from '@/view/rechargeMoney'
import coinRecord from '@/view/coinRecord'
import noticeList from '@/components/noticeList'
import noticeDetail from '@/components/noticeDetail'
import aceite from '@/view/aceite'
import region from '@/view/region'
import chargeRecord from '@/view/chargeRecord'
import new_account from '@/components/new_account'
import finance from '@/view/accounts/finance'
import transfer from '@/view/accounts/transfer'
import lever from '@/view/accounts/lever'
import finanrec from '@/view/accounts/finanrec'
import manger from '@/view/accounts/lever_manger'
import legal from '@/view/accounts/legal'
import change from '@/view/accounts/change'
import center from '@/view/accounts/center'
import TradePassword from "@/view/accounts/TradePassword"
import assetsIndex from '@/view/assets/index'
import overviewAssets from '@/view/assets/overviewAssets'
import spotAccount from '@/view/assets/spotAccount'
import contractAccount from '@/view/assets/contractAccount'
import optionAccount from '@/view/assets/optionAccount'
import fiatAccount from '@/view/assets/fiatAccount'
import financialAccount from '@/view/assets/financialAccount'
import assetAccount from '@/view/assets/assetAccount'
import orderCenter from '@/view/assets/orderCenter'
import orderTransfer from '@/view/assets/orderTransfer'
import fiat from '@/view/accounts/fiat'
import UOrder from '@/view/accounts/UOrder'
import suggestionsAndFeedback from '@/view/accounts/suggestionsAndFeedback'
import currencyApplication from '@/view/accounts/currencyApplication'
import legalAccount from '@/view/accounts/legalAccount'
import changeAccount from '@/view/accounts/changeAccount'
import transferLegal from '@/view/accounts/transferLegal'
import transferLog from '@/view/transfer_log'
import newsList from '@/view/newsList'
import news from '@/view/news'
import allTransaction from '@/view/all_transaction'
import leverTransactions from '@/view/lever_transactions'
import leverTransaction from '@/view/lever/lever_transaction'
import leverList from '@/view/lever/leverList'
import transferRecord from '@/view/lever/transferRecord'
import success from '@/view/success'
import quotation from '@/components/quotation'
import ForgetPwd from '@/components/ForgetPwd' //忘记密码
import ResetPwd from '@/components/ResetPwd' //重置密码密码
import withdraw_address from '@/view/accounts/withdraw_address'
import letransactions from '@/view/accounts/letransactions'
import leverRecord from '../view/lever/leverRecord.vue'
import bindEmail from '../view/bindEmail.vue'
import secondContract from "@/components/secondContract";
import Pledge from "@/components/pledge";
import iepnList from "@/components/iepnList";
import text from '@/view/text'
import marketQuotaion from '@/view/marketQuotaion'
import aiIndex from '@/view/aiIndex'
import portfolioOrder from '@/view/portfolioOrder'
import c2cIndex from '@/view/c2cIndex'
import merchantList from '@/view/c2c/merchantList'
import merchantOrder from '@/view/c2c/merchantOrder'
import myOrders from '@/view/c2c/myOrders'
import buyingCoinsOrder from '@/view/buyingCoinsOrder'
import currencyPurchase from '@/view/currencyPurchase'
import documentary from '@/view/documentary'
import documentaryRecord from '@/view/documentaryRecord'
import followOrderDetails from '@/view/followOrderDetails'
import borrowMoney from '@/view/borrowMoney'
import portfolio from '@/view/portfolio'
import activityIndex from '@/view/activityIndex'
import activityVip from '@/view/activityVip'
import flashRight from '@/view/flashRight'
import fastTransaction from '@/view/fastTransaction'
import creditCard from '@/view/creditCard'
import accountNewPage from '@/view/accountNewPage'
import defi from '@/view/defi'
import defidetail from '@/view/defidetail'
import earn from '@/view/earn'
import record from '@/view/record'
import structured_financial from '@/view/structured_financial'
import startup from '@/view/startup'
import startupDetail from '@/view/startupDetail'
import help from '@/view/help'
import message from '@/view/message'
import messageDetail from '@/view/messageDetail'
import agreement from '@/view/agreement'
import download from '@/view/download'
import download2 from '@/view/download2'
import deposit from '@/view/deposit'
import withdrawDepositNew from "@/view/withdrawDepositNew"
import invitation from '@/view/invitation'
import history from '@/view/history'
import transferSet from '@/view/transferSet'
import modifyPassword from '@/view/modifyPassword'
import setTransactionPassword from '@/view/setTransactionPassword'
import credit from '@/view/credit'
import bonus from '@/view/bonus'
import bonusRecord from '@/view/bonusRecord'
import secondDealCenter from '@/components/secondDealCenter'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: home,
children: [{
path: '',
name: 'homeContent',
component: homeContent,
meta: {
head: true
}
},
{
path: '/match',
component: () => import('../components/match.vue')
},
{
path: '/fiatradCenter',
name: 'fiatradCenter',
component: merchantList
},
{
path: '/userSetting',
name: 'userSetting',
component: userSetting,
children: [{
path: '',
component: payOpts
}]
},
{
path: '/c2c',
name: 'c2c',
component: merchantList,
},
{
path: '/dealCenter',
name: 'dealCenter',
component: dealCenter
},
{
path: '/dealCenterOld',
name: 'dealCenterOld',
component: dealCenterOld
},
{
path: '/quotation',
name: 'quotation',
component: quotation
},
{
path: '/secondDealCenterOld',
name: 'secondContract',
component: secondContract
},
{
path: '/secondDealCenter',
name: 'secondDealCenter',
component: () => import('@/view/option/index'),
meta: {
head: false
}
},
{
path: '/leverdealCenter',
name: 'leverdealCenter',
component: leverdealCenterV2
},
{
path: '/leverdealCenterOld',
name: 'leverdealCenterOld',
component: leverdealCenter
},
{
path: '/allTransaction',
name: 'allTransaction',
component: allTransaction
},
{
path: '/leverRecord',
component: leverRecord
},
{
path: '/bindEmail',
component: bindEmail
},
{
path: '/leverTransactions',
name: 'leverTransactions',
component: leverTransactions
},
{
path: '/leverTransaction',
name: 'leverTransaction',
component: leverTransaction
},
{
path: '/leverList',
name: 'leverList',
component: leverList
},
{
path: '/transferRecord',
name: 'transferRecord',
component: transferRecord
},
{
path: '/success',
name: 'success',
component: success
}
]
},
{
path: '/components/login',
name: 'login',
component: login
},
{
path: '/forgetPwd',
name: 'forgetPwd',
component: ForgetPwd
},
{
path: '/resetPwd',
name: 'resetPwd',
component: ResetPwd
},
{
path: '/components/register',
name: 'register',
component: register
},
{
path: '/noticeList',
name: 'noticeList',
component: noticeList
},
{
path: '/upstart',
name: 'upStar',
component: iepnList
},
{
path: '/pledge',
name: 'pledge',
component: Pledge
},
{
path: '/components/noticeDetail',
name: 'noticeDetail',
component: noticeDetail
},
{
path: '/account',
name: 'account',
component: account,
children: [{
path: '/accountCenter',
name: 'accountCenter',
component: accountCenter
},
{
path: '/accountEntrust',
name: 'accountEntrust',
component: accountEntrust
},
{
path: '/accountMessage',
name: 'accountMessage',
component: accountMessage
},
{
path: '/coinRecord',
name: 'coinRecord',
component: coinRecord
},
{
path: '/rechargeMoney',
name: 'rechargeMoney',
component: rechargeMoney
},
{
path: '/aceite',
name: 'aceite',
component: aceite
},
{
path: '/region',
name: 'region',
component: region
},
{
path: '/chargeRecord',
name: 'chargeRecord',
component: chargeRecord
},
{
path: '/userSetting',
name: 'userSetting',
component: payOpts,
},
{
path: '/authentication',
name: 'authentication',
component: authentication
},
{
path: '/advanced',
name: 'advanced',
component: advanced
},
{
path: '/payment',
name: 'payment',
component: payment
}
]
},
{
path: '/assetsIndex',
name: 'assetsIndex',
component: assetsIndex,
children: [{
path: 'overviewAssets',
name: 'overviewAssets',
component: overviewAssets,
meta: {
type: '0'
}
},
{
path: 'spotAccount',
name: 'spotAccount',
component: spotAccount,
meta: {
type: '1'
}
},
{
path: 'contractAccount',
name: 'contractAccount',
component: contractAccount,
meta: {
type: '2'
}
},
{
path: 'optionAccount',
name: 'optionAccount',
component: optionAccount,
meta: {
type: '3'
}
},
{
path: 'financialAccount',
name: 'financialAccount',
component: financialAccount,
meta: {
type: '4'
}
},
{
path: 'fiatAccount',
name: 'fiatAccount',
component: fiatAccount,
meta: {
type: '5'
}
},
{
path: 'assetAccount',
name: 'assetAccount',
component: assetAccount,
meta: {
type: '4'
}
},
{
path: 'orderCenter',
name: 'orderCenter',
component: orderCenter,
meta: {
type: '6'
}
},
{
path: 'orderTransfer',
name: 'orderTransfer',
component: orderTransfer,
meta: {
type: '8'
}
}
]
},
{
path: '/new_account',
name: 'new_account',
component: new_account,
children: [
//jqk
{
path: '/finance',
name: 'finance',
component: finance
},
{
path: '/letransactions',
name: 'letransactions',
component: letransactions,
},
{
path: '/transfer',
name: 'transfer',
component: transfer
},
{
path: '/lever',
name: 'lever',
component: lever
},
{
path: '/change',
name: 'change',
component: change
},
{
path: '/tradePassword',
name: 'tradePassword',
component: TradePassword
},
{
path: '/center',
name: 'center',
component: center
},
{
path: '/fiat',
name: 'fiat',
component: fiat
},
{
path: '/UOrder',
name: 'UOrder',
component: UOrder
},
{
path: '/suggestionsAndFeedback',
name: 'suggestionsAndFeedback',
component: suggestionsAndFeedback
},
{
path: '/currencyApplication',
name: 'currencyApplication',
component: currencyApplication
},
{
path: '/legal',
name: 'legal',
component: legal
},
{
path: '/legalAccount',
name: 'legalAccount',
component: legalAccount
},
{
path: '/changeAccount',
name: 'changeAccount',
component: changeAccount
},
{
path: '/transferLegal',
name: 'transferLegal',
component: transferLegal
}
]
},
// 合约管理
{
path: '/lever_manger',
name: 'manger',
component: manger,
},
// 财务记录
{
path: '/finanrec',
name: 'finanrec',
component: finanrec,
},
// 地址管理
{
path: '/withdraw_address',
name: 'withdraw_address',
component: withdraw_address,
},
//123
{
path: '/setCash',
name: 'setCash',
component: setCash
},
{
path: '/text',
name: 'text',
component: text
},
{
path: '/marketQuotaion',
name: 'marketQuotaion',
component: marketQuotaion
},
{
path: '/aiIndex',
name: 'aiIndex',
component: aiIndex
},
{
path: '/c2cIndex',
name: 'c2cIndex',
component: merchantList
},
{
path: '/c2cMerchant',
name: 'c2cMerchant',
component: merchantList
},
{
path: '/c2cMerchantOrder',
name: 'c2cMerchantOrder',
component: merchantOrder
},
{
path: '/c2cMyOrders',
name: 'c2cMyOrders',
component: myOrders
},
{
path: '/portfolioOrder',
name: 'portfolioOrder',
component: portfolioOrder
},
{
path: '/buyingCoinsOrder',
name: 'buyingCoinsOrder',
component: buyingCoinsOrder
},
{
path: '/currencyPurchase',
name: 'currencyPurchase',
component: currencyPurchase
},
{
path: '/documentary',
name: 'documentary',
component: documentary
},
{
path: '/documentaryRecord',
name: 'documentaryRecord',
component: documentaryRecord
},
{
path: '/followOrderDetails',
name: 'followOrderDetails',
component: followOrderDetails
},
{
path: '/portfolio',
name: 'portfolio',
component: portfolio
},
{
path: '/activityIndex',
name: 'activityIndex',
component: activityIndex
},
{
path: '/activityVip',
name: 'activityVip',
component: activityVip
},
{
path: '/flashRight',
name: 'flashRight',
component: flashRight,
},
{
path: '/borrowMoney',
name: 'borrowMoney',
component: borrowMoney
},
{
path: '/fastTransaction',
name: 'fastTransaction',
component: fastTransaction
},
{
path: '/creditCard',
name: 'creditCard',
component: creditCard
},
{
path: '/accountNewPage',
name: 'accountNewPage',
component: accountNewPage
},
{
path: '/mx-defi',
name: 'mx-defi',
component: defi
},
{
path: '/earn',
name: 'earn',
component: earn
},
{
path: '/mx-defi-detail',
name: 'mx-defi-detail',
component: defidetail
},
{
path: '/structured_financial',
name: 'structured_financial',
component: structured_financial
},
{
path: '/record',
name: 'record',
component: record
},
{
path: '/startup',
name: 'startup',
component: startup
},
{
path: '/startupDetail',
name: 'startupDetail',
component: startupDetail
},
{
path: '/help',
name: 'help',
component: help
},
{
path: '/message',
name: 'message',
component: message
},
{
path: '/messageDetail',
name: 'messageDetail',
component: messageDetail
},
{
path: '/agreement',
name: 'agreement',
component: agreement
},
{
path: '/download',
name: 'download',
component: download
},
{
path: '/download2',
name: 'download2',
component: download2
},
{
path: '/deposit',
name: 'deposit',
component: deposit
},
// {
// path: '/withdrawDeposit',
// name: 'withdrawDeposit',
// component: withdrawDeposit
// },
// 提现
{
path: '/withdrawDeposit',
name: 'withdrawDeposit',
component: withdrawDepositNew
},
{
path: '/invitation',
name: 'invitation',
component: invitation
},
{
path: '/history',
name: 'history',
component: history
},
{
path: '/transferSet',
name: 'transferSet',
component: transferSet
},
{
path: '/transferLog',
name: 'transferLog',
component: transferLog
},
{
path: '/newsList',
name: 'newsList',
component: newsList
},
{
path: '/news',
name: 'news',
component: news
},
{
path: '/accountSet',
name: 'accountSet',
component: accountSet
},
{
path: '/modifyPassword',
name: 'modifyPassword',
component: modifyPassword
},
{
path: '/setTransactionPassword',
name: 'setTransactionPassword',
component: setTransactionPassword
},
{
path: '/credit',
name: 'credit',
component: credit
},
{
path: '/bonus',
name: 'bonus',
component: bonus
},
{
path: '/bonusRecord',
name: 'bonusRecord',
component: bonusRecord
},
{
path: '/loan',
name: 'loan',
component: () => import('@/view/loan/index')
},
{
path: '/ieo',
name: 'ieo',
component: () => import('@/view/ieo/index')
},
{
path: '/ieo/detail',
name: 'ieoDetail',
component: () => import('@/view/ieo/detail')
},
{
path: '/optionTrade',
name: 'optionTrade',
component: () => import('@/view/option/index')
},
{
path: '/creditCenter',
name: 'creditCenter',
component: () => import('@/view/credit/index')
},
{
path: '/creditIeo',
name: 'creditIeo',
component: () => import('@/view/credit/ieoList')
},
]
})
+554
View File
@@ -0,0 +1,554 @@
<template>
<div id="c2c-box" class="flex">
<div class="c2c-l">
<left></left>
</div>
<div class="c2c-r">
<div class="listbox">
<div class="flex flextitle">
<span>{{$t('c2c.type')}}</span>
<span class="tc">{{$t('c2c.num')}}</span>
<span class="tc">{{$t('legal.totalmoney')}}</span>
<span class="tc">{{$t('c2c.payType')}}</span>
<span class="tc">{{$t('time')}}</span>
<span class="tc">{{$t('status')}}</span>
<span class="tr">{{$t('do')}}</span>
</div>
<ul>
<li v-for="(item,index) in list" :key="index" class="flex alcenter curPer">
<div class="flex alcenter">
<p class="head">{{item.type=='sell'?$t('c2c.sell'):$t('c2c.buy')}}</p>
<span class="currencyName ml5">{{item.currency_name}}</span>
</div>
<div class="flex center tc">
<span class="light_blue sellerName">{{item.deal_money || '0.00' | tofixed}}</span>
</div>
<div class="tc">{{item.price || '0.00' | tofixed}}</div>
<div class="tc">
<img v-if="item.way_name == $t('legal.alipay')" src="../../assets/images/zfb_icon.png">
<img v-if="item.way_name == $t('legal.wechat')" src="../../assets/images/wx_icon.png">
<img v-if="item.way_name == $t('c2c.bankcard')" src="../../assets/images/bank_icon.png">
</div>
<div class="tc">{{item.create_time}}</div>
<div class="tc">
<div v-if="item.is_sure==0">{{$t('legal.nofinish')}}</div>
<div v-if="item.is_sure==1">{{$t('legal.finished')}}</div>
<div v-if="item.is_sure==2">{{$t('legal.ceiled')}}</div>
<div v-if="item.is_sure==3">{{$t('legal.payed')}}</div>
</div>
<div class="tr" @click="modals(item.id)">
<button class="detail-btn" type="button">{{$t('c2c.detail')}}</button>
</div>
</li>
</ul>
</div>
</div>
<!-- 详情弹窗 -->
<div class="modals flex" v-show="modalShow">
<div class="modals-content">
<div class="modal-header">
<h3>{{$t('c2c.detail')}}</h3>
<p @click="closes()">X</p>
</div>
<!-- 不带按钮的订单详情 -->
<div class="pay-cannel" v-show="payContent.type == 'buy'" v-if="payContent.type == 'buy'">
<div>
<span class="modals-left">{{$t('status')}}</span>
<span v-if="payContent.is_sure == 0">{{$t('legal.nofinish')}}</span>
<span v-if="payContent.is_sure == 1">{{$t('legal.finished')}}</span>
<span v-if="payContent.is_sure == 2">{{$t('legal.ceiled')}}</span>
<span v-if="payContent.is_sure == 3">{{$t('legal.payed')}}</span>
</div>
<div>
<span class="modals-left">{{$t('legal.total')}}</span>
{{payContent.deal_money || '0.00' | tofixed}}
</div>
<div>
<span class="modals-left">{{$t('c2c.name')}}</span>
{{payContent.user_cash_info.real_name}}
</div>
<div v-if="payContent.user_cash_info.bank_account">
<span class="modals-left">{{$t('set.cnum')}}</span>
{{payContent.user_cash_info.bank_account}}
</div>
<div v-if="payContent.user_cash_info.wechat_account">
<span class="modals-left">{{$t('c2c.wx')}}</span>
{{payContent.user_cash_info.wechat_account}}
</div>
<div v-if="payContent.user_cash_info.alipay_account">
<span class="modals-left">{{$t('c2c.ailipay')}}:</span>
{{payContent.user_cash_info.alipay_account}}
</div>
<div>
<span class="modals-left">{{$t('c2c.connect')}}</span>
{{payContent.phone}}
</div>
</div>
<!-- 带按钮的订单状态 -->
<div
class="pay-complete"
v-show="payContent.type == 'sell'"
v-if="payContent.type == 'sell'"
>
<div>
<span class="modals-left">{{$t('status')}}</span>
<span v-if="payContent.is_sure == 0">{{$t('legal.nofinish')}}</span>
<span v-if="payContent.is_sure == 1">{{$t('legal.finished')}}</span>
<span v-if="payContent.is_sure == 2">{{$t('legal.ceiled')}}</span>
<span v-if="payContent.is_sure == 3">{{$t('legal.payed')}}</span>
</div>
<div>
<span class="modals-left">{{$t('legal.total')}}</span>
{{payContent.deal_money || '0.00' | tofixed}}
</div>
<div>
<span class="modals-left">{{$t('price')}}</span>
{{payContent.price || '0.00' | tofixed}}
</div>
<div>
<span class="modals-left">{{$t('number')}}</span>
{{payContent.number || '0.00' | tofixed}}{{payContent.currency_name}}
</div>
<div>
<span class="modals-left">{{$t('time')}}</span>
{{payContent.format_create_time}}
</div>
<div>
<span class="modals-left">{{$t('c2c.connect')}}参考号</span>
{{payContent.id}}
</div>
<div>
<span class="modals-left">{{$t('c2c.connect')}}</span>
{{payContent.phone}}
</div>
</div>
<!-- 按钮 -->
<div class="btns" v-show="modalBtnShow">
<button type="button" @click="comfirm(payContent.id)">{{$t('legal.surepay')}}</button>
</div>
<div class="btns" v-show="pannelShow">
<button type="button" @click="cannel(payContent.id)">{{$t('legal.orderceil')}}</button>
<button type="button" @click="payComfirm(payContent.id)">{{$t('legal.mypayed')}}</button>
</div>
</div>
</div>
</div>
</template>
<script>
import left from "@/view/c2c/leftc2c";
import Vue from "vue";
Vue.filter("circle", function(value) {
return value.substring(0, 1);
});
export default {
components: { left },
data() {
return {
token: "",
current: 0,
type: "sell",
page: 1,
list: [],
legal_id: "",
classify: "求购",
ids: "",
payContent: {},
modalShow: false,
pannelShow: false,
modalBtnShow: false
};
},
filters: {
tofixed: function(val) {
val = Number(val);
return val.toFixed(2);
},
tofixedFour: function(val) {
val = Number(val);
return val.toFixed(4);
}
},
created() {
let that = this;
that.token = window.localStorage.getItem("token") || "";
that.ids = this.$route.query.id;
if (that.token == "") {
this.$router.push("/components/login");
}
that.getList(that.ids);
},
methods: {
changeType(index, type, title) {
this.current = index;
this.classify = title;
this.type = type;
this.list = [];
this.page = 1;
this.getList(type);
},
// 获取买入列表
getList(ids) {
let that = this;
let page = 1;
let i = layer.load();
this.$http({
url: "/api/c2c/legal_send_deal_list?&page=" + that.page + "&id=" + ids,
method: "get",
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
let listdata = res.data.message.data;
if (listdata.length != 0) {
this.list = this.list.concat(listdata);
this.page += 1;
}
}
});
},
// 下单
order(id) {
var that = this;
layer.confirm(
"确认下单吗?",
{
btn: [that.$t('confirm'), that.$t('legal.ceil')] //按钮
},
function() {
that.sureOrder(id);
},
function() {
// layer.msg('取消成功');
}
);
},
// 下单请求
sureOrder(id) {
var i = layer.load();
this.$http({
url: "/api/c2c/do_legal_deal",
method: "post",
data: { id: id },
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
layer.msg(res.data.message);
} else {
layer.msg(res.data.message);
}
});
},
// 弹窗内容
modals(ids) {
let that = this;
let i = layer.load();
this.$http({
url: "/api/c2c_deal?&id=" + ids,
method: "get",
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
that.modalShow = true;
that.payContent = res.data.message;
// 判断按钮是否显示
if (res.data.message.is_sure == 3 &&res.data.message.type == "sell") {
that.modalBtnShow = true;
} else {
that.modalBtnShow = false;
}
if (res.data.message.is_sure == 0 && res.data.message.type == "buy") {
that.pannelShow = true;
} else {
that.pannelShow = false;
}
}
});
},
// 关闭弹窗
closes() {
this.modalShow = false;
},
// 确认收款
comfirm(ids) {
let that = this;
layer.confirm(
"请确认买家已向您付款?",
{
btn: [that.$t('legal.surepay'), that.$t('legal.ceil')] //按钮
},
function() {
let i = layer.load();
that.$http({
url: "/api/c2c/legal_deal_sure",
method: "post",
data: { id: ids },
headers: { Authorization: that.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
layer.msg(res.data.message);
that.modalShow = false;
setTimeout(function() {
that.getList(that.ids);
}, 500);
} else {
layer.msg(res.data.message);
}
});
},
function() {
// layer.msg('取消成功');
}
);
},
// 取消订单
cannel(ids){
let that = this;
layer.confirm(
that.$t('legal.ceilnot'),
{
btn: ["取消确认", that.$t('legal.ceil')] //按钮
},
function() {
let i = layer.load();
that.$http({
url: "/api/c2c/user_legal_pay_cancel",
method: "post",
data: { id: ids },
headers: { Authorization: that.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
layer.msg(res.data.message);
that.modalShow = false;
setTimeout(function() {
that.getList(that.ids);
}, 500);
} else {
layer.msg(res.data.message);
}
});
},
function() {
// layer.msg('取消成功');
}
);
},
// 买家付款确认
payComfirm(ids){
let that = this;
layer.confirm(
"付款确认后请尽快向卖家付款,恶意点击将直接冻结账户?",
{
btn: ["付款确认", "取消"] //按钮
},
function() {
let i = layer.load();
that.$http({
url: "/api/c2c/user_legal_pay",
method: "post",
data: { id: ids },
headers: { Authorization: that.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
layer.msg(res.data.message);
that.modalShow = false;
setTimeout(function() {
that.getList(that.ids);
}, 500);
} else {
layer.msg(res.data.message);
}
});
},
function() {
// layer.msg('取消成功');
}
);
},
}
};
</script>
<style lang='scss'>
#c2c-box {
margin: 10px 0 10px;
color: #c7cce6;
.ml5 {
margin-left: 5px;
}
.detail-btn{
border-radius: 3px;
color: white;
background-color: #638BD4;
cursor: pointer;
min-height: 33px;
min-width: 80px;
font-size: 14px;
font-weight: 600;
border: none;
}
.c2c-r {
.title-top {
p {
margin-right: 30px;
padding: 5px 0;
cursor: pointer;
}
}
.coin-select {
p {
margin-right: 10px;
cursor: pointer;
}
.select {
color: #7a98f7;
}
}
.listbox {
.flextitle {
padding: 20px 0;
border-bottom: 1px solid #242840;
span {
flex: 1;
}
}
li {
padding: 15px 0;
> div {
flex: 1;
line-height: 36px;
}
.head {
width: 36px;
height: 36px;
border-radius: 50%;
margin-right: 10px;
background: #5d8cc2;
color: #fff;
text-align: center;
font-size: 14px;
}
.btn {
border-radius: 3px;
color: white;
background-color: #638bd4;
cursor: pointer;
min-height: 33px;
min-width: 80px;
font-size: 14px;
font-weight: 600;
border: none;
}
}
li:nth-child(even) {
background: #242840;
}
li:last-child {
border-bottom: 1px solid #242840;
}
}
}
> .c2c-l {
margin: 0 10px;
padding: 10px;
background: #181b2a;
width: 23%;
li {
padding: 0 20px;
justify-content: space-between;
cursor: pointer;
.redColor {
margin-left: 10px;
}
&:hover {
background: rgb(38, 42, 66);
}
}
}
> .c2c-r {
padding: 10px 30px;
background: #181b2a;
margin-right: 10px;
width: 77%;
}
// 弹窗
.modals {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.3);
overflow: hidden;
justify-content: center;
align-items: center;
.modals-content {
width: 500px;
background-color: #262a42;
margin: 0 auto;
border-radius: 5px;
.modal-header {
line-height: 40px;
text-align: center;
position: relative;
p {
position: absolute;
right: 10px;
top: 0;
}
}
.pay-cannel {
margin: 0 15px;
padding-bottom: 10px;
div {
line-height: 40px;
}
}
.pay-complete {
margin: 0 15px;
padding-bottom: 10px;
div {
line-height: 40px;
}
}
.modals-left {
width: 100px;
display: inline-block;
}
.btns {
line-height: 40px;
margin: 0 15px;
padding-bottom: 10px;
button {
border-radius: 3px;
color: white;
background-color: #638bd4;
cursor: pointer;
min-height: 33px;
min-width: 80px;
font-size: 14px;
font-weight: 600;
border: none;
padding: 0 10px;
}
}
}
}
}
</style>
// WEBPACK FOOTER //
// src/view/c2c/c2cDetail.vue
+638
View File
@@ -0,0 +1,638 @@
<template>
<div id="c2c-box" class="flex">
<div class="c2c-l">
<left></left>
</div>
<div class="c2c-r">
<div class="title-top flex ft20">
<p v-for="(item,index) in topType" :key="index" :class="{'active':index==current}" @click='changeType(index,item.type,item.title)'>{{item.title}}</p>
</div>
<div class="coin-select mt20 flex">
<p v-for="(item,index) in coinList" :key="index" :class="{'select':index==select}" @click="getnew(index,item.id)">{{item.name}}</p>
</div>
<div class="listbox">
<div class="flex flextitle">
<span>{{$t('legal.shoper')}}</span>
<span class="tc">{$t('c2c.num')}}</span>
<span class="tc">{{$t('c2c.onePrice')}}</span>
<span class="tc">{{$t('c2c.payType')}}</span>
<span class="tr">{{$t('c2c.opera')}}</span>
</div>
<ul>
<li v-for="(item,index) in list" :key="index" class="flex alcenter curPer">
<div class="flex alcenter">
<p class="head" :style="{background:item.bgcolor}">{{item.seller_name | circle}}</p>
<p class="blue">{{item.seller_name}}</p>
</div>
<div class="flex center tc">
<span class="sellerName">{{item.surplus_number || '0.00' |tofixed}}</span>
<span class="currencyName ml5">{{item.currency_name}}</span>
</div>
<div class="tc light_green ft16">{{item.price || '0.00' |tofixed}}</div>
<div class="tc">
<img v-if="item.way == 'ali_pay'" src="../../assets/images/zfb_icon.png" />
<img v-if="item.way == 'we_chat'" src="../../assets/images/wx_icon.png" />
<img v-if="item.way == 'bank'" src="../../assets/images/bank_icon.png" />
</div>
<div class="tr">
<button class="btn" @click="order(item.id)">{{classify}}</button>
</div>
</li>
<div class="tc ft12 gray mt20 curPer" v-if="list.length>=10&&showmore" @click="getMore()">{{$t('more')}}...</div>
</ul>
</div>
</div>
</div>
</template>
<script>
import left from '@/view/c2c/leftc2c'
import Vue from 'vue'
Vue.filter('circle', function (value) {
return value.substring(0,1)
})
export default {
components:{left},
data(){
return{
token:'',
current:0,
select:0,
type:'sell',
page:1,
coinList:[],
list:[],
legal_id:'',
classify:this.$t(legal.buy),
topType:[{'title':this.$t(legal.buy),"type":"sell"},{'title':this.$t(legal.sell),"type":"buy"}],
bgcolor:['#5d8cc2','#6d78a8','#a2b240','#61b88e','#e35744', '#a16c92','#66756e'],
showmore:true
}
},
filters: {
tofixed: function(val) {
val = Number(val);
return val.toFixed(2);
},
tofixedFour: function(val) {
val = Number(val);
return val.toFixed(4);
}
},
created(){
this.token = window.localStorage.getItem("token") || "";
if (this.token == "") {
this.$router.push("/components/login");
};
this.get_currency();
},
methods:{
changeType(index,type,title){
this.current=index;
this.classify=title;
this.type=type;
this.list=[];
this.page=1;
this.getList();
},
getnew(index,id){
this.select=index;
this.legal_id=id;
this.page = 1;
this.list=[];
this.getList();
},
// 获取币种列表
get_currency() {
this.$http({
url: "/api/currency/list",
method: "get",
headers: { Authorization: this.token }
}).then(res => {
if (res.data.type == "ok") {
this.coinList = res.data.message.legal;
this.legal_id=res.data.message.legal[0].id;
this.getList('sell');
}
});
},
// 获取买入列表
getList() {
let type = this.type;
let page = this.page;
let legal_id =this.legal_id;
let i = layer.load();
this.$http({
url: "/api/c2c_deal_platform?type=" + type + "&page=" + page+"&currency_id="+legal_id,
method: "get",
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
let listdata = res.data.message.data;
if (listdata.length != 0) {
for(var j=0; j< listdata.length; j++) {
listdata[j].bgcolor = this.bgcolor[(Math.floor(Math.random() * 6 + 1)-1)];
}
this.list = this.list.concat(listdata);
this.page += 1;
}
if(listdata.length<10){
this.showmore=false
}
}
});
},
getMore(){
this.getList();
},
// 下单
order(id){
var that = this;
layer.confirm('确认下单吗', {
btn: ['确认','取消'] //按钮
}, function(){
that.sureOrder(id);
}, function(){
// layer.msg('取消成功');
});
},
// 下单请求
sureOrder(id){
var that = this;
var i=layer.load();
this.$http({
url: "/api/c2c/do_legal_deal",
method: "post",
data:{id:id},
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
layer.msg(res.data.message.msg);
that.page=1;
that.getList();
}else{
layer.msg(res.data.message)
}
});
}
},
}
</script>
<style lang='scss' scoped>
#c2c-box {
margin: 10px 0 10px;
color: #c7cce6;
.ml5{margin-left: 5px}
.c2c-r{
.title-top {
p{
margin-right: 30px;
padding: 5px 0;
cursor: pointer;
}
}
.coin-select{
p{
margin-right: 10px;
cursor: pointer;
}
.select{
color: #7a98f7;
}
}
.listbox{
.flextitle{
padding: 20px 0;
border-bottom: 1px solid #242840;
span{
flex:1;
}
}
li{
padding: 10px 0;
>div{
flex: 1;
// line-height: 36px;
}
.blue{
color: #3b68bb;
font-weight: bolder;
}
.light_green{
color: #489972;
font-weight: 600;
}
.head{
width: 36px;
height: 36px;
border-radius: 50%;
margin-right: 10px;
background: #5D8CC2;
color: #fff;
text-align: center;
font-size: 14px;
}
.btn{
border-radius: 3px;
color: white;
background-color: #638BD4;
cursor: pointer;
min-height: 33px;
min-width: 80px;
font-size: 14px;
font-weight: 600;
border: none;
}
}
li:nth-child(even){
background: #242840;
}
li:last-child{
border-bottom: 1px solid #242840;
}
}
}
.mask {
position: fixed;
width: 100%;
height: 100%;
z-index: 999;
color: #333;
background: rgba(0, 0, 0, 0.7);
> .m-content {
border-radius: 4px;
background: #fff;
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
padding: 40px 20px 30px;
// min-height: 400px;
max-height: 550px;
width: 400px;
> .title {
position: absolute;
top: 0;
left: 0;
text-align: center;
width: 100%;
height: 40px;
line-height: 40px;
font-size: 18px;
text-align: center;
font-weight: 600;
> div:last-child {
position: absolute;
top: 0;
right: 0;
padding: 0 15px;
cursor: pointer;
}
}
> div:not(.title) {
line-height: 32px;
// border-top: 1px solid #eaecef;
}
div {
span:first-child {
margin-right: 5px;
display: inline-block;
width: 89px;
color: #7a98f7;
}
}
}
}
.more {
color: #7a98f7;
text-align: center;
cursor: pointer;
}
font-size: 14px;
> .c2c-l {
margin: 0 10px;
padding: 10px;
background: #181b2a;
width: 23%;
li {
// padding: 0 10px;
padding: 0 20px;
justify-content: space-between;
cursor: pointer;
line-height: 40px;
.redColor {
margin-left: 10px;
}
&:hover {
background: rgb(38, 42, 66);
}
}
}
> .c2c-r {
padding: 10px 30px;
background: #181b2a;
margin-right: 10px;
width: 77%;
> .top {
// margin: 10px 30px 10px;
> .top-title {
font-size: 24px;
line-height: 2;
.link-span {
cursor: pointer;
&:hover {
color: #7a98f7;
}
}
}
.top-price {
> div {
margin-right: 20px;
// line-height: 1.8;
> span:last-child {
font-weight: 600;
font-size: 16px;
}
}
}
.inp-items {
> div:first-child {
margin-right: 30px;
border-right: 1px dashed #4e5b85;
padding-right: 30px;
}
> .inp-item {
flex: 1;
> .inp-title {
border-bottom: 1px solid #4e5b85;
font-size: 16px;
justify-content: space-between;
line-height: 3;
}
> .how {
line-height: 40px;
cursor: pointer;
}
> .three-inp {
> .inp-box {
position: relative;
height: 42px;
line-height: 40px;
padding-left: 112px;
margin-bottom: 15px;
> span {
position: absolute;
width: 112px;
// text-align: center;
top: 0;
left: 0;
}
> div,
> input {
color: #c7cce6;
display: block;
width: 100%;
line-height: 40px;
padding: 0 20px;
// background: #1e2235;
border: 1px solid #4e5b85;
background: none;
}
> div {
color: #7a98f7;
}
}
}
.pay-opts {
flex-wrap: wrap;
> div:nth-child(n + 2) {
margin-left: 15px;
}
> div {
// padding-right: 20px;
line-height: 40px;
> input {
vertical-align: middle;
}
}
}
> .btn-in,
.btn-out {
margin-top: 20px;
line-height: 42px;
text-align: center;
cursor: pointer;
color: #fff;
background: #4e5b85;
}
}
}
}
> .bot {
color: #6b80ae;
// margin: 10px 30px;
// background: #181b2a;
> .bot-title {
margin: 30px 0 0;
// border-bottom: 1px solid #ccc;
font-size: 16px;
line-height: 40px;
justify-content: space-between;
align-items: center;
> div:first-child {
cursor: pointer;
span {
font-weight: 600;
line-height: 40px;
margin-right: 20px;
}
.active {
color: #7a98f7;
border: none;
}
}
> .flex {
height: 17px;
line-height: 15px;
cursor: pointer;
> div {
margin-right: 10px;
border: 1px solid #ccc;
transition: all 0.3s;
width: 32px;
border-radius: 7.5px;
div {
width: 15px;
height: 15px;
border-radius: 50%;
background: #fff;
}
}
.switch-on {
padding-left: 0;
background: #ccc;
}
.switch {
transition: all 0.3s;
background: #7a98f7;
padding-left: 15px;
}
}
}
> .list-title {
height: 40px;
// justify-content: space-between;
line-height: 40px;
font-weight: 600;
}
.list-title,
ul li .content {
width: 100%;
color: #c7cce6;
justify-content: space-between;
// cursor: pointer;
text-align: center;
padding: 8px 5px;
line-height: 24px;
> div:first-child {
width: 6%;
height: 24px;
text-align: left;
}
> div:nth-child(n + 2) {
width: 12%;
}
> div:last-child {
// min-width: 19%;
width: 24%;
}
> .last {
text-align: right;
height: 24px;
> div,
> span {
float: right;
margin: 0 5px;
}
.btn-last {
padding: 0 10px;
min-width: 55px;
max-width: 80px;
color: #fff;
// margin-left: 70px;
text-align: center !important;
cursor: pointer;
}
}
}
ul li .trade-list {
background: #262a42;
width: 100%;
padding: 5px 30px 10px;
margin: 0 auto;
p {
font-weight: 600;
// font-size: 20px;
// font-size: 16px;
line-height: 36px;
span {
flex: 1;
text-align: center;
}
span:first-child {
text-align: left;
}
span:last-child {
text-align: right;
}
}
ul {
li {
padding: 12px 0;
border-bottom: 1px solid #303956;
> div {
text-align: center;
width: 25%;
}
> div:first-child {
text-align: left;
}
> div:last-child {
text-align: right;
div {
float: right;
padding: 0 14px;
margin: 0 6px;
line-height: 1.6;
cursor: pointer;
}
}
}
}
}
.ul-out li .content {
> div:first-child {
color: #25796a;
}
.btn-last {
background: #25796a;
}
}
.ul-in li .content {
> div:first-child {
color: #7a98f7;
}
.btn-last {
background: #7a98f7;
}
}
}
}
}
.detailit,
.showtrade {
// float: right;
margin-right: 20px;
padding: 0 10px;
min-width: 55px;
max-width: 80px;
color: #fff;
text-align: center !important;
cursor: pointer;
background: #7a98f7;
}
</style>
// WEBPACK FOOTER //
// src/view/c2c/c2cOrder.vue
+317
View File
@@ -0,0 +1,317 @@
<template>
<div id="c2c-box" class="flex">
<div class="c2c-l">
<left></left>
</div>
<div class="c2c-r">
<div class="top">
<div class="top-left">
<h4>购买</h4>
<ul>
<li
v-for="(item,index) in currency_list"
:key="index"
:class="index == active?'bg_active':''"
:data-id="item.id"
@click="currency_click(item.id,item.name,index,'buy')"
>{{item.name}}</li>
</ul>
</div>
<div class="top-right">
<h4>出售</h4>
<ul>
<li
v-for="(item,index) in currency_list"
:key="index"
:class="index == actives?'bg_active':''"
:data-id="item.id"
@click="currency_click(item.id,item.name,index,'sell')"
>{{item.name}}</li>
</ul>
</div>
</div>
<div class="bot">
<ul class="content" v-if="buyList.length>0">
<li class="flex">
<div class="tl">名称</div>
<div>数量</div>
<div>单价</div>
<div>付款方式</div>
<div class="tr">操作</div>
</li>
<li v-for="item in buyList" :key="item.id" class="flex">
<div class="tl">{{item.currency_name}}</div>
<div>{{item.total_number || '0.00' | tofixed}} {{item.currency_name}}</div>
<div>{{item.price || '0.00' | tofixed}}</div>
<div>
<img v-if="item.way == 'ali_pay'" src="../../assets/images/zfb_icon.png">
<img v-if="item.way == 'we_chat'" src="../../assets/images/wx_icon.png">
<img v-if="item.way == 'bank'" src="../../assets/images/bank_icon.png">
</div>
<div class="tr">
<button type="button" @click="backOrder(item.id)">撤回</button>
<button type="button" @click="lookOrder(item.id)">查看订单</button>
</div>
</li>
</ul>
<div class="no_data tc" v-if="buyList.length<=0">
<img src="../../assets/images/nodata.png" alt>
<p class="fColor2 ft18">暂无数据</p>
</div>
<button class="more" @click="moreData()">{{moreText}}</button>
</div>
</div>
</div>
</template>
<script>
import left from "@/view/c2c/leftc2c";
export default {
components: {
left
},
data() {
return {
token: "",
active: 0,
actives:-1,
currency_list: [],
currency_name: "",
id: "",
showList: true,
showDetail: false,
detail: {}, //li详情,
showTradeBox: false,
page: 1,
types: "buy",
buyList: [],
moreText: "加载更多"
};
},
filters: {
tofixed: function(val) {
val = Number(val);
return val.toFixed(2);
},
tofixedFour: function(val) {
val = Number(val);
return val.toFixed(4);
}
},
created() {
this.token = window.localStorage.getItem("token") || "";
if (this.token == "") {
this.$router.push("/components/login");
}
this.get_currency();
},
methods: {
// 获取币种列表
get_currency() {
let i = layer.load();
this.$http({
url: "/api/currency/list",
method: "get",
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
this.currency_list = res.data.message.legal;
this.currency_name = res.data.message.legal[0].name;
this.id = res.data.message.legal[0].id;
this.getBuyList();
}
});
},
//选择币种
currency_click(id, name, index,type) {
this.currency_name = name;
this.id = id;
this.types = type;
if(type == 'buy'){
this.active = index;
this.actives = -1;
}else{
this.actives = index;
this.active = -1;
}
this.page = 1;
this.getBuyList();
},
// 获取购买币种数据列表
getBuyList() {
let i = layer.load();
let that = this;
that.$http({
url: "/api/c2c/seller_trade?type=" + that.types + "&page=" + that.page+"&currency_id="+that.id,
method: "get",
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
let listData = res.data.message.data;
if (listData.length > 0) {
that.buyList = that.buyList.concat(listData);
} else if (that.page == 1 && listData.length == 0) {
that.moreText = '';
}else{
that.moreText = '没有更多数据';
}
}
});
},
// 撤回订单
backOrder(ids) {
let i = layer.load();
this.$http({
url: "/api/c2c/back_send",
method: "post",
data: {
id: ids
},
headers: { Authorization: this.token }
})
.then(res => {
layer.close(i);
layer.msg(res.data.message);
setTimeout(() => {
location.reload();
}, 500);
})
.catch(res => {
layer.msg(res.data.message);
});
},
// 加载更多
moreData() {
let that = this;
that.page = that.page + 1;
that.getBuyList();
},
// 查看订单
lookOrder(ids){
this.$router.push({name:'c2cDetail',query:{id:ids}});
}
}
};
</script>
<style lang='scss'>
#c2c-box {
margin: 10px 0 10px;
.more {
color: #7a98f7;
text-align: center;
cursor: pointer;
width: 100%;
text-align: center;
border: none;
background-color: rgba(0,0,0,0);
}
> .c2c-l {
margin: 0 10px;
padding: 10px;
background: #181b2a;
width: 23%;
}
> .c2c-r {
padding: 10px 30px;
background: #181b2a;
margin-right: 10px;
width: 77%;
> .top {
// margin: 10px 30px 10px;
> .top-title {
font-size: 24px;
line-height: 2;
}
.top-left{
width: 49.55%;
display: inline-block;
color: #c7cce6;
font-size: 15px;
h4{
font-size: 17px;
}
li{
display: inline-block;
margin-top: 10px;
margin-right: 5px;
}
.bg_active {
color: #7a98f7;
border-bottom: 1px solid #7a98f7;
}
}
.top-right{
width: 49.55%;
display: inline-block;
color: #c7cce6;
h4{
font-size: 17px;
}
li{
display: inline-block;
margin-top: 10px;
margin-right: 5px;
}
.bg_active {
color: #7a98f7;
border-bottom: 1px solid #7a98f7;
}
}
}
> .bot {
color: #6b80ae;
.content {
width: 100%;
li {
width: 100%;
color: #c7cce6;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
text-align: center;
padding: 8px 5px;
line-height: 24px;
}
> .flex {
cursor: pointer;
> div {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
padding: 10px 0;
}
}
.tl {
text-align: left;
}
.tr {
text-align: right;
}
button {
border-radius: 3px;
color: white;
background-color: #638bd4;
cursor: pointer;
min-height: 33px;
min-width: 80px;
font-size: 14px;
font-weight: 600;
border: none;
}
}
}
}
}
</style>
// WEBPACK FOOTER //
// src/view/c2c/c2cRelease.vue
+476
View File
@@ -0,0 +1,476 @@
<template>
<div id="c2c-box" class="flex">
<div class="c2c-l">
<left></left>
</div>
<div class="c2c-r">
<!-- <div class="title-top flex ft20">
<p v-for="(item,index) in topType" :class="{'active':index==current}" @click='changeType(index,item.type,item.title)'>{{item.title}}</p>
</div> -->
<div class="listbox">
<div class="flex flextitle">
<span>类型</span>
<span class="tc">数量</span>
<span class="tc">交易总额</span>
<span class="tc">支付方式</span>
<span class="tc">时间</span>
<span class="tc">状态</span>
<span class="tr">操作</span>
</div>
<ul>
<li v-for="(item,index) in list" :key="index" class="flex alcenter curPer">
<div class="flex alcenter">
<p :class="['head',{'sell ': item.type =='buy'}]">{{item.type=='sell'?'购买':'出售'}}</p>
<span class="currencyName ml5 blue">{{item.currency_name}}</span>
</div>
<div class="tc">
<span>{{item.number || '0.00' | tofixed}}</span>
</div>
<div class="tc light_green">{{item.deal_money || '0.00' | tofixed}}</div>
<div class="tc">
<img v-if="item.way_name == '支付宝'" src="../../assets/images/zfb_icon.png" />
<img v-if="item.way_name == '微信'" src="../../assets/images/wx_icon.png" />
<img v-if="item.way_name == '1'" src="../../assets/images/bank_icon.png" />
</div>
<div class="tc">{{item.create_time}}</div>
<div class="tc">
<div v-if="item.is_sure==0">未完成</div>
<div v-if="item.is_sure==1">已完成</div>
<div v-if="item.is_sure==2">已取消</div>
<div v-if="item.is_sure==3">已付款</div>
</div>
<div class="tr">
<button class="btn" @click="getDetail(item.id)">详情</button>
</div>
</li>
<div class="tc ft12 gray mt20 curPer" v-if="list.length>=10&&showmore" @click="getMore()">加载更多...</div>
</ul>
</div>
</div>
<!-- =========详情弹窗========== -->
<div class="mask" v-if="showDetail">
<div class="m-content">
<div class="title">
<div>详情</div>
<div @click="showDetail = false">x</div>
</div>
<div v-if="detail.is_sure==0">
<span>状态:</span><span>待付款</span>
</div>
<div v-if="detail.is_sure==1">
<span>状态:</span><span>已完成</span>
</div>
<div v-if="detail.is_sure==2">
<span>状态:</span><span>已取消</span>
</div>
<div v-if="detail.is_sure==3">
<span>状态:</span><span>已付款</span>
</div>
<div v-if="detail.type=='sell'">
<div>
<span>卖家</span><span>{{detail.seller_name}}</span>
</div>
<div>
<span>付款信息</span><span>{{detail.way_name}}</span>
</div>
<div>
<span>账号</span>
<span v-if="detail.way_name=='支付宝'">{{detail.sell_cash_info.alipay_account}}</span>
<span v-if="detail.way_name=='微信'">{{detail.sell_cash_info.wechat_account}}</span>
</div>
<div>
<span>银行卡号</span><span>{{detail.sell_cash_info.bank_name}}:{{detail.sell_cash_info.bank_account}}</span>
</div>
<div>
<span>收款人</span><span>{{detail.hes_realname}}</span>
</div>
</div>
<div>
<span>联系方式</span><span>{{detail.seller_phone}}</span>
</div>
<div>
<span>数量</span><span>{{detail.number || '0.00' | tofixed}}</span>
</div>
<div>
<span>单价</span><span>{{detail.price || '0.00' | tofixed}}</span>
</div>
<div>
<span>总额</span><span>{{detail.deal_money || '0.00' | tofixed}}</span>
</div>
<div>
<span>下单时间</span><span>{{detail.create_time}}</span>
</div>
<div>
<span>参考号:</span><span>{{detail.id}}</span>
</div>
<!-- btnc -->
<div class="flex around btnbox mt20" v-if="detail.is_sure==0&&detail.type=='sell'">
<div class="ceilorder tc" @click="ceilOrder(detail.id)">取消订单</div>
<div class="surepay tc" @click="sureOrder(detail.id)">我已付款点击确认</div>
</div>
<div class="mt20 btnbox" v-if="detail.is_sure==3&&detail.type=='buy'">
<div class="surepay tc" @click="surePayed(detail.id)">确认已收款</div>
</div>
</div>
</div>
</div>
</template>
<script>
import left from '@/view/c2c/leftc2c'
import Vue from 'vue'
Vue.filter('circle', function (value) {
return value.substring(0,1)
})
export default {
components:{left},
data(){
return{
token:'',
current:0,
type:'sell',
page:1,
list:[],
legal_id:'',
classify:'求购',
topType:[{'title':"求购","type":"buy"},{'title':"出售","type":"sell"}],
showDetail:false,
detail:[],
showmore:true
}
},
filters: {
tofixed: function(val) {
val = Number(val);
return val.toFixed(2);
},
tofixedFour: function(val) {
val = Number(val);
return val.toFixed(4);
}
},
created(){
this.token = window.localStorage.getItem("token") || "";
if (this.token == "") {
this.$router.push("/components/login");
};
this.getList();
},
methods:{
changeType(index,type,title){
this.current=index;
this.classify=title;
this.type=type;
this.list=[];
this.page=1;
this.getList(type);
},
// 获取买入列表
getList() {
let i = layer.load();
this.$http({
url: "/api/c2c_user_deal?&page=" + this.page,
method: "get",
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
let listdata = res.data.message.data;
if (listdata.length != 0) {
this.list = this.list.concat(listdata);
this.page += 1;
}
if(listdata.length<10){
this.showmore=false
}
}
});
},
// 加载更多
getMore(){
this.getList();
},
// 详情
getDetail(id) {
var i=layer.load();
var that =this;
this.$http({
url: "/api/c2c_deal?id=" + id,
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
this.detail=res.data.message;
this.showDetail = true;
}
});
},
// 取消订单
ceilOrder(id){
var that = this;
layer.confirm('确认取消交易', {
btn: ['确认','取消'] //按钮
}, function(){
that.orderCeil(id);
}, function(){
// layer.msg('取消成功');
});
},
orderCeil(id){
var that = this;
var i=layer.load();
this.$http({
url: "/api/c2c/user_legal_pay_cancel",
method: "post",
data:{id:id},
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
layer.msg(res.data.message);
that.page=1;
that.getList();
that.showDetail = false;
}else{
layer.msg(res.data.message)
}
});
},
// 确认我已付款
sureOrder(id){
var that = this;
layer.confirm('请确认您已向卖家付款,恶意点击将被冻结账户', {
btn: ['确认','取消'] //按钮
}, function(){
that.orderSure(id);
}, function(){
// layer.msg('取消成功');
});
},
orderSure(id){
var that = this;
var i=layer.load();
this.$http({
url: "/api/c2c/user_legal_pay",
method: "post",
data:{id:id},
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
layer.msg(res.data.message);
that.page=1;
that.getList();
that.showDetail = false;
}else{
layer.msg(res.data.message);
}
});
},
// 确认我已收款
surePayed(id){
var that = this;
layer.confirm('请确认您已收买家付款信息', {
btn: ['确认','取消'] //按钮
}, function(){
that.payedSure(id);
}, function(){
// layer.msg('取消成功');
});
},
payedSure(id){
var that = this;
var i=layer.load();
this.$http({
url: "/api/c2c/legal_deal_user_sure",
method: "post",
data:{id:id},
headers: { Authorization: this.token }
}).then(res => {
layer.close(i);
if (res.data.type == "ok") {
layer.msg(res.data.message);
that.page=1;
that.getList();
that.showDetail = false;
}else{
layer.msg(res.data.message);
}
});
}
},
}
</script>
<style lang='scss'>
#c2c-box {
margin: 10px 0 10px;
color: #c7cce6;
.ml5{margin-left: 5px}
.c2c-r{
.title-top {
p{
margin-right: 30px;
padding: 5px 0;
cursor: pointer;
}
}
.coin-select{
p{
margin-right: 10px;
cursor: pointer;
}
.select{
color: #7a98f7;
}
}
.listbox{
.flextitle{
padding: 20px 0;
border-bottom: 1px solid #242840;
span{
flex:1;
}
}
li{
padding: 10px 0;
>div{
flex: 1;
// line-height: 36px;
}
.blue{
color: #3b68bb;
font-weight: bolder;
}
.light_green{
color: #489972;
font-weight: 600;
}
.head{
width: 36px;
height: 36px;
border-radius: 50%;
margin-right: 10px;
background: #5D8CC2;
color: #fff;
text-align: center;
font-size: 14px;
line-height: 36px;
}
.sell{
background: #e35744;
}
.btn{
border-radius: 3px;
color: white;
background-color: #638BD4;
cursor: pointer;
min-height: 33px;
min-width: 80px;
font-size: 14px;
font-weight: 600;
border: none;
}
}
li:nth-child(even){
background: #242840;
}
li:last-child{
border-bottom: 1px solid #242840;
}
}
}
> .c2c-l {
margin: 0 10px;
padding: 10px;
background: #181b2a;
width: 23%;
li {
padding: 0 20px;
justify-content: space-between;
cursor: pointer;
.redColor {
margin-left: 10px;
}
&:hover {
background: rgb(38, 42, 66);
}
}
}
> .c2c-r {
padding: 10px 30px;
background: #181b2a;
margin-right: 10px;
width: 77%;
}
.mask {
position: fixed;
width: 100%;
height: 100%;
left: 0;
top: 0;
z-index: 999;
background: rgba(0, 0, 0, 0.7);
> .m-content {
border-radius: 4px;
background: #262a42;
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
padding: 40px 20px 30px;
max-height: 550px;
width: 400px;
> .title {
position: absolute;
top: 0;
left: 0;
text-align: center;
width: 100%;
height: 40px;
line-height: 40px;
font-size: 18px;
text-align: center;
font-weight: 600;
> div:last-child {
position: absolute;
top: 0;
right: 0;
padding: 0 15px;
cursor: pointer;
}
}
> div:not(.title) {
line-height: 32px;
}
div {
span:first-child {
margin-right: 5px;
display: inline-block;
width: 90px;
// color: #ca4141;
}
}
.btnbox{
>div{
padding: 3px 15px;
background: #689CF1;
border-radius: 4px;
color: #fff;
cursor: pointer;
}
.ceilorder{
background: #9DB5C7;
}
}
}
}
}
</style>
// WEBPACK FOOTER //
// src/view/c2c/c2cTrade.vue
+35
View File
@@ -0,0 +1,35 @@
function injectStyle (ssrContext) {
require("!!../../../node_modules/extract-text-webpack-plugin/dist/loader.js?{\"omit\":1,\"remove\":true,\"publicPath\":\"../../\"}!vue-style-loader!css-loader?{\"sourceMap\":true}!../../../node_modules/vue-loader/lib/style-compiler/index?{\"vue\":true,\"id\":\"data-v-35ec98b0\",\"scoped\":true,\"hasInlineConfig\":false}!sass-loader?{\"sourceMap\":true}!sass-resources-loader?{\"resources\":\"C:\\\\Users\\\\17563\\\\Documents\\\\WeChat Files\\\\wxid_1wlsabavb7ok21\\\\FileStorage\\\\File\\\\2020-09\\\\v2\\\\src\\\\assets\\\\style\\\\mixin.scss\"}!../../../node_modules/vue-loader/lib/selector?type=styles&index=0!./leftc2c.vue")
}
var normalizeComponent = require("!../../../node_modules/vue-loader/lib/component-normalizer")
/* script */
export * from "!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./leftc2c.vue"
import __vue_script__ from "!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./leftc2c.vue"
/* template */
import __vue_template__ from "!!../../../node_modules/vue-loader/lib/template-compiler/index?{\"id\":\"data-v-35ec98b0\",\"hasScoped\":true,\"transformToRequire\":{\"video\":[\"src\",\"poster\"],\"source\":\"src\",\"img\":\"src\",\"image\":\"xlink:href\"},\"buble\":{\"transforms\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./leftc2c.vue"
/* template functional */
var __vue_template_functional__ = false
/* styles */
var __vue_styles__ = injectStyle
/* scopeId */
var __vue_scopeId__ = "data-v-35ec98b0"
/* moduleIdentifier (server only) */
var __vue_module_identifier__ = null
var Component = normalizeComponent(
__vue_script__,
__vue_template__,
__vue_template_functional__,
__vue_styles__,
__vue_scopeId__,
__vue_module_identifier__
)
export default Component.exports
//////////////////
// WEBPACK FOOTER
// ./src/view/c2c/leftc2c.vue
// module id = null
// module chunks =
+809
View File
@@ -0,0 +1,809 @@
<template>
<div class="merchant-list-wrap">
<indexHeader></indexHeader>
<secondaryHead :text="$t('c2cMerchant.merchant')"></secondaryHead>
<div class="bit-center-wrap ml-box">
<!-- Top Bar: Tabs + Order Link -->
<div class="top-bar">
<!-- Buy/Sell Tab -->
<div class="direction-tab">
<div
class="tab-item"
:class="{ active: direction === 'buy', buy: direction === 'buy' }"
@click="switchDirection('buy')"
>
{{ $t('c2cMerchant.buy') }}
</div>
<div
class="tab-item"
:class="{ active: direction === 'sell', sell: direction === 'sell' }"
@click="switchDirection('sell')"
>
{{ $t('c2cMerchant.sell') }}
</div>
</div>
<div class="order-link" @click="$router.push('/c2cMyOrders')">
<i class="el-icon-document"></i> {{ $t('c2cMerchant.order_records') || '訂單記錄' }}
</div>
</div>
<!-- Filter Bar -->
<div class="filter-bar">
<div class="filter-item">
<span class="label">{{ $t('market.currency') }}</span>
<el-select v-model="cryptoCurrency" size="small" @change="loadList">
<el-option
v-for="item in cryptoOptions"
:key="item"
:label="item"
:value="item"
></el-option>
</el-select>
</div>
<div class="filter-item">
<span class="label">{{ $t('indexHeader.fiat_currency_transactions') }}</span>
<el-select v-model="fiatCurrency" size="small" @change="loadList">
<el-option
v-for="item in fiatOptions"
:key="item"
:label="item"
:value="item"
></el-option>
</el-select>
</div>
</div>
<!-- PC Table -->
<div class="table-wrap pc-only">
<div class="table-head">
<span class="col-merchant">{{ $t('c2cMerchant.merchant') }}</span>
<span class="col-qty">{{ $t('c2cMerchant.quantity') }} / {{ $t('c2cMerchant.limit') }}</span>
<span class="col-price">{{ $t('c2cMerchant.unit_price') }}</span>
<span class="col-pay">{{ $t('c2cMerchant.payment') }}</span>
<span class="col-rate">{{ $t('c2cMerchant.rating') }}</span>
<span class="col-op">{{ $t('c2cMerchant.action') || '操作' }}</span>
</div>
<div v-if="list.length === 0" class="empty">{{ $t('batchData.沒有更多了') || '暂无数据' }}</div>
<div v-for="item in list" :key="item.id" class="table-row">
<div class="col-merchant">
<img v-if="item.logo" :src="item.logo" class="avatar-img" />
<div v-else class="avatar" :style="{ background: avatarColor(item.name) }">
{{ (item.name || 'M').substring(0, 1).toUpperCase() }}
</div>
<div class="m-info">
<div class="m-name">{{ item.name }}</div>
</div>
</div>
<div class="col-qty">
<div class="qty">{{ $t('c2cMerchant.quantity') }} {{ formatQty(item.available_qty) }} {{ item.crypto_currency }}</div>
<div class="limit">
{{ $t('c2cMerchant.limit') }} {{ formatQty(item.min_amount) }} - {{ formatQty(item.max_amount) }} {{ item.crypto_currency }}
</div>
</div>
<div class="col-price">
<span class="price">{{ formatNum(item.unit_price) }}</span>
<span class="fiat">{{ item.fiat_currency }}</span>
</div>
<div class="col-pay">
<span v-for="(p, idx) in parsePayments(item.payment_methods)" :key="idx" class="pay-tag">
{{ p }}
</span>
</div>
<div class="col-rate">
<span class="rate">{{ item.rating || '100' }}%</span>
</div>
<div class="col-op">
<button
class="op-btn"
:class="direction === 'buy' ? 'btn-buy' : 'btn-sell'"
@click="openOrder(item)"
>
{{ direction === 'buy' ? $t('c2cMerchant.buy_crypto') : $t('c2cMerchant.sell_crypto') }}
</button>
</div>
</div>
</div>
<!-- H5 Card List -->
<div class="card-list h5-only">
<div v-if="list.length === 0" class="empty">{{ $t('batchData.沒有更多了') || '暂无数据' }}</div>
<div v-for="item in list" :key="'c-' + item.id" class="card">
<div class="card-head">
<img v-if="item.logo" :src="item.logo" class="avatar-img" />
<div v-else class="avatar" :style="{ background: avatarColor(item.name) }">
{{ (item.name || 'M').substring(0, 1).toUpperCase() }}
</div>
<div class="m-name">{{ item.name }}</div>
<div class="rate">{{ $t('c2cMerchant.rating') }} | {{ item.rating || '100' }}%</div>
</div>
<div class="card-row">
<span>{{ $t('c2cMerchant.unit_price') }}</span>
<span class="price">{{ formatNum(item.unit_price) }} {{ item.fiat_currency }}</span>
</div>
<div class="card-row">
<span>{{ $t('c2cMerchant.quantity') }}</span>
<span>{{ formatNum(item.available_qty) }} {{ item.crypto_currency }}</span>
</div>
<div class="card-row">
<span>{{ $t('c2cMerchant.limit') }}</span>
<span>{{ formatNum(item.min_amount) }} - {{ formatNum(item.max_amount) }} {{ item.fiat_currency }}</span>
</div>
<div class="card-row">
<span>{{ $t('c2cMerchant.payment') }}</span>
<span class="pay">
<span v-for="(p, idx) in parsePayments(item.payment_methods)" :key="idx" class="pay-tag">
{{ p }}
</span>
</span>
</div>
<button
class="card-btn"
:class="direction === 'buy' ? 'btn-buy' : 'btn-sell'"
@click="openOrder(item)"
>
{{ direction === 'buy' ? $t('c2cMerchant.buy_crypto') : $t('c2cMerchant.sell_crypto') }}
</button>
</div>
</div>
<!-- 3 Step Cards -->
<div class="steps-wrap">
<div class="step-card">
<div class="step-num">1</div>
<div class="step-title">{{ $t('c2cMerchant.step1') }}</div>
<div class="step-desc">{{ $t('c2cMerchant.step1_desc') }}</div>
</div>
<div class="step-card">
<div class="step-num">2</div>
<div class="step-title">{{ $t('c2cMerchant.step2') }}</div>
<div class="step-desc">{{ $t('c2cMerchant.step2_desc') }}</div>
</div>
<div class="step-card">
<div class="step-num">3</div>
<div class="step-title">{{ $t('c2cMerchant.step3') }}</div>
<div class="step-desc">{{ $t('c2cMerchant.step3_desc') }}</div>
</div>
</div>
</div>
<indexFooter class="pc-only"></indexFooter>
<!-- Order Dialog -->
<el-dialog
:title="dialogTitle"
:visible.sync="dialogVisible"
:width="dialogWidth"
custom-class="merchant-dialog"
:close-on-click-modal="false"
>
<div v-if="currentMerchant" class="dialog-body">
<div class="d-row">
<span class="d-label">{{ $t('c2cMerchant.merchant') }}</span>
<span class="d-value">{{ currentMerchant.name }}</span>
</div>
<div class="d-row">
<span class="d-label">{{ $t('c2cMerchant.unit_price') }}</span>
<span class="d-value price">
{{ formatNum(currentMerchant.unit_price) }} {{ currentMerchant.fiat_currency }}
</span>
</div>
<div class="d-row">
<span class="d-label">{{ $t('c2cMerchant.limit') }}</span>
<span class="d-value">
{{ formatNum(currentMerchant.min_amount) }} - {{ formatNum(currentMerchant.max_amount) }}
{{ currentMerchant.fiat_currency }}
</span>
</div>
<div class="d-input">
<span class="d-label">{{ $t('c2cMerchant.enter_amount') }}</span>
<el-input
v-model="orderQuantity"
type="number"
:placeholder="$t('c2cMerchant.enter_amount')"
>
<template slot="append">{{ currentMerchant.crypto_currency }}</template>
</el-input>
</div>
<div class="d-row total">
<span class="d-label">{{ $t('c2cMerchant.total') }}</span>
<span class="d-value">
{{ computedTotal }} {{ currentMerchant.fiat_currency }}
</span>
</div>
<div class="d-actions">
<button class="btn-cancel" @click="dialogVisible = false">
{{ $t('c2cMerchant.cancel') || '取消' }}
</button>
<button
class="btn-confirm"
:class="direction === 'buy' ? 'btn-buy' : 'btn-sell'"
@click="submitOrder"
>
{{ $t('c2cMerchant.confirm_order') }}
</button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import indexHeader from '@/view/indexHeader'
import indexFooter from '@/view/indexFooter'
import secondaryHead from '@/components/secondaryHead'
export default {
name: 'merchantList',
components: { indexHeader, indexFooter, secondaryHead },
data() {
return {
token: '',
direction: 'buy',
cryptoCurrency: 'USDC',
fiatCurrency: 'USD',
cryptoOptions: ['USDC'],
fiatOptions: ['USD', 'CNY', 'HKD', 'EUR'],
list: [],
dialogVisible: false,
currentMerchant: null,
orderQuantity: '',
palette: ['#00c48c', '#3b68bb', '#e35744', '#a16c92', '#66756e', '#5d8cc2', '#a2b240']
}
},
computed: {
dialogTitle() {
return this.direction === 'buy'
? this.$t('c2cMerchant.buy_crypto')
: this.$t('c2cMerchant.sell_crypto')
},
dialogWidth() {
return window.innerWidth < 800 ? '92%' : '420px'
},
computedTotal() {
if (!this.currentMerchant) return '0.00'
const q = parseFloat(this.orderQuantity) || 0
const p = parseFloat(this.currentMerchant.unit_price) || 0
return (q * p).toFixed(2)
}
},
created() {
this.token = window.localStorage.getItem('token') || ''
this.loadList()
},
methods: {
formatNum(v) {
if (v === undefined || v === null || v === '') return '0.00'
const n = Number(v)
if (isNaN(n)) return '0.00'
return n.toFixed(2)
},
formatQty(v) {
if (v === undefined || v === null || v === '') return '0'
const n = Number(v)
if (isNaN(n)) return '0'
return n.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 2 })
},
avatarColor(name) {
if (!name) return this.palette[0]
let sum = 0
for (let i = 0; i < name.length; i++) sum += name.charCodeAt(i)
return this.palette[sum % this.palette.length]
},
parsePayments(pm) {
if (!pm) return []
if (Array.isArray(pm)) return pm
if (typeof pm === 'string') {
try {
const arr = JSON.parse(pm)
return Array.isArray(arr) ? arr : [pm]
} catch (e) {
return pm.split(',').map(s => s.trim()).filter(Boolean)
}
}
return []
},
switchDirection(d) {
if (this.direction === d) return
this.direction = d
this.loadList()
},
loadList() {
const i = typeof layer !== 'undefined' ? layer.load() : null
this.$http({
url: '/api/c2c_merchant/list',
method: 'get',
params: {
direction: this.direction,
crypto_currency: this.cryptoCurrency,
fiat_currency: this.fiatCurrency
},
headers: { Authorization: this.token }
})
.then(res => {
if (i !== null) layer.close(i)
if (res.data.type === 'ok') {
const msg = res.data.message
this.list = Array.isArray(msg) ? msg : msg && msg.data ? msg.data : []
} else {
this.list = []
}
})
.catch(() => {
if (i !== null) layer.close(i)
this.list = []
})
},
openOrder(item) {
if (!this.token) {
this.$router.push('/components/login')
return
}
this.currentMerchant = item
this.orderQuantity = ''
this.dialogVisible = true
},
openContact(merchant) {
if (!merchant) return
const type = (merchant.contact_type || '').toLowerCase()
const link = merchant.contact_link || ''
if (!link) return
if (type === 'whatsapp') {
const num = (link + '').replace(/[^0-9]/g, '')
if (num) window.open('https://wa.me/' + num, '_blank')
} else if (type === 'telegram') {
const handle = (link + '').replace(/^@/, '')
window.open('https://t.me/' + handle, '_blank')
} else if (link.indexOf('http') === 0) {
window.open(link, '_blank')
} else {
window.open('https://' + link, '_blank')
}
},
submitOrder() {
if (!this.currentMerchant) return
const q = parseFloat(this.orderQuantity)
if (!q || q <= 0) {
if (typeof layer !== 'undefined') layer.msg(this.$t('c2cMerchant.enter_amount'))
return
}
const min = parseFloat(this.currentMerchant.min_amount) || 0
const max = parseFloat(this.currentMerchant.max_amount) || 0
if (min && q < min) {
if (typeof layer !== 'undefined') {
layer.msg(`${this.$t('c2cMerchant.limit')}: ${min} ${this.currentMerchant.crypto_currency}`)
}
return
}
if (max && q > max) {
if (typeof layer !== 'undefined') {
layer.msg(`${this.$t('c2cMerchant.limit')}: ${max} ${this.currentMerchant.crypto_currency}`)
}
return
}
const i = typeof layer !== 'undefined' ? layer.load() : null
this.$http({
url: '/api/c2c_merchant/create_order',
method: 'post',
data: {
merchant_id: this.currentMerchant.id,
direction: this.direction,
amount: q,
crypto_currency: this.currentMerchant.crypto_currency,
fiat_currency: this.currentMerchant.fiat_currency
},
headers: { Authorization: this.token }
})
.then(res => {
if (i !== null) layer.close(i)
if (res.data.type === 'ok') {
this.dialogVisible = false
this.openContact(this.currentMerchant)
} else {
if (typeof layer !== 'undefined') layer.msg(res.data.message || 'error')
}
})
.catch(() => {
if (i !== null) layer.close(i)
})
}
}
}
</script>
<style lang="scss" scoped>
.merchant-list-wrap {
min-height: 100vh;
background: #0e1726;
color: #c7cce6;
}
.ml-box {
padding: 20px 0 60px;
}
.top-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.order-link {
color: #00c48c;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
&:hover { text-decoration: underline; }
i { font-size: 15px; }
}
.avatar-img {
width: 36px;
height: 36px;
border-radius: 50%;
object-fit: cover;
margin-right: 10px;
}
.pc-only {
display: block;
}
.h5-only {
display: none;
}
/* Direction Tabs */
.direction-tab {
display: flex;
gap: 12px;
.tab-item {
padding: 10px 32px;
border-radius: 6px;
background: #12141e;
border: 1px solid #242840;
color: #848e9c;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: all 0.15s;
&.active.buy {
background: #00c48c;
color: #fff;
border-color: #00c48c;
}
&.active.sell {
background: #ff4040;
color: #fff;
border-color: #ff4040;
}
}
}
/* Filter Bar */
.filter-bar {
display: flex;
gap: 16px;
margin-bottom: 24px;
.filter-item {
display: flex;
align-items: center;
gap: 8px;
.label {
color: #848e9c;
font-size: 13px;
}
}
}
/* PC Table */
.table-wrap {
background: #12141e;
border-radius: 8px;
border: 1px solid #242840;
overflow: hidden;
margin-bottom: 30px;
.table-head {
display: flex;
padding: 14px 20px;
border-bottom: 1px solid #242840;
background: #0e1726;
color: #848e9c;
font-size: 13px;
> span {
flex: 1;
&.col-merchant { flex: 1.4; }
&.col-op { flex: 0.8; text-align: right; }
}
}
.empty {
padding: 40px 0;
text-align: center;
color: #848e9c;
}
.table-row {
display: flex;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid #1a1f30;
transition: background 0.15s;
&:hover {
background: #1a1f30;
}
> div {
flex: 1;
}
.col-merchant {
flex: 1.4;
display: flex;
align-items: center;
.avatar {
width: 36px;
height: 36px;
border-radius: 50%;
color: #fff;
text-align: center;
line-height: 36px;
font-weight: 600;
margin-right: 10px;
}
.m-name {
color: #fff;
font-weight: 600;
font-size: 14px;
}
.m-orders {
color: #848e9c;
font-size: 12px;
margin-top: 2px;
}
}
.col-qty {
.qty { color: #fff; font-size: 13px; }
.limit { color: #848e9c; font-size: 12px; margin-top: 2px; }
}
.col-price {
.price { color: #00c48c; font-size: 18px; font-weight: 600; }
.fiat { color: #848e9c; font-size: 12px; margin-left: 4px; }
}
.col-pay {
.pay-tag {
display: inline-block;
padding: 2px 8px;
margin-right: 6px;
font-size: 12px;
background: #1a1f30;
border-radius: 3px;
color: #c7cce6;
}
}
.col-rate .rate { color: #c7cce6; font-size: 13px; }
.col-op {
flex: 0.8;
text-align: right;
}
}
}
.op-btn {
padding: 8px 22px;
border-radius: 4px;
color: #fff;
border: none;
cursor: pointer;
font-size: 14px;
font-weight: 600;
&.btn-buy { background: #00c48c; }
&.btn-sell { background: #ff4040; }
&:hover { opacity: 0.85; }
}
/* H5 Card List */
.card-list {
.empty {
padding: 40px 0;
text-align: center;
color: #848e9c;
}
.card {
background: #12141e;
border: 1px solid #242840;
border-radius: 8px;
padding: 14px;
margin-bottom: 12px;
.card-head {
display: flex;
align-items: center;
padding-bottom: 10px;
border-bottom: 1px solid #1a1f30;
margin-bottom: 10px;
.avatar-img {
width: 32px;
height: 32px;
border-radius: 50%;
object-fit: cover;
margin-right: 10px;
}
.avatar {
width: 32px;
height: 32px;
border-radius: 50%;
color: #fff;
text-align: center;
line-height: 32px;
font-weight: 600;
margin-right: 10px;
}
.m-name { flex: 1; color: #fff; font-size: 14px; font-weight: 600; }
.rate { color: #00c48c; font-size: 12px; }
}
.card-row {
display: flex;
justify-content: space-between;
padding: 6px 0;
font-size: 13px;
> span:first-child { color: #848e9c; }
> span:last-child { color: #c7cce6; text-align: right; }
.price { color: #00c48c; font-weight: 600; font-size: 15px; }
.pay-tag {
display: inline-block;
padding: 1px 6px;
margin-left: 4px;
background: #1a1f30;
border-radius: 3px;
color: #c7cce6;
font-size: 11px;
}
}
.card-btn {
width: 100%;
margin-top: 10px;
padding: 10px 0;
border: none;
border-radius: 4px;
color: #fff;
font-size: 14px;
font-weight: 600;
cursor: pointer;
&.btn-buy { background: #00c48c; }
&.btn-sell { background: #ff4040; }
}
}
}
/* Step Cards */
.steps-wrap {
display: flex;
gap: 16px;
margin-top: 30px;
.step-card {
flex: 1;
padding: 20px;
background: #12141e;
border: 1px solid #242840;
border-radius: 8px;
.step-num {
width: 32px;
height: 32px;
line-height: 32px;
text-align: center;
background: #00c48c;
color: #fff;
border-radius: 50%;
font-weight: 600;
margin-bottom: 10px;
}
.step-title {
color: #fff;
font-size: 15px;
font-weight: 600;
margin-bottom: 6px;
}
.step-desc {
color: #848e9c;
font-size: 12px;
line-height: 1.6;
}
}
}
/* Dialog */
.dialog-body {
color: #c7cce6;
.d-row {
display: flex;
justify-content: space-between;
padding: 8px 0;
font-size: 14px;
.d-label { color: #848e9c; }
.d-value { color: #fff; &.price { color: #00c48c; font-weight: 600; } }
&.total {
padding-top: 14px;
margin-top: 14px;
border-top: 1px solid #242840;
font-size: 15px;
.d-value { color: #00c48c; font-weight: 600; }
}
}
.d-input {
margin: 16px 0;
.d-label { color: #848e9c; font-size: 13px; display: block; margin-bottom: 8px; }
}
.d-actions {
display: flex;
gap: 10px;
margin-top: 20px;
> button {
flex: 1;
padding: 10px 0;
border: none;
border-radius: 4px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
}
.btn-cancel {
background: #242840;
color: #c7cce6;
}
.btn-confirm.btn-buy { background: #00c48c; color: #fff; }
.btn-confirm.btn-sell { background: #ff4040; color: #fff; }
}
}
@media screen and (max-width: 800px) {
.pc-only { display: none !important; }
.h5-only { display: block !important; }
.ml-box {
padding: 10px 12px 40px;
}
.direction-tab {
gap: 8px;
.tab-item {
flex: 1;
text-align: center;
padding: 10px 0;
font-size: 14px;
}
}
.filter-bar {
gap: 10px;
flex-wrap: wrap;
.filter-item {
flex: 1;
min-width: 40%;
}
}
.steps-wrap {
flex-direction: column;
gap: 10px;
margin-top: 20px;
.step-card {
padding: 14px;
}
}
}
</style>
<style lang="scss">
.merchant-dialog {
background: #12141e !important;
border: 1px solid #242840;
.el-dialog__header {
border-bottom: 1px solid #242840;
padding: 16px 20px;
.el-dialog__title { color: #fff; font-size: 16px; font-weight: 600; }
.el-dialog__headerbtn .el-dialog__close { color: #848e9c; }
}
.el-dialog__body {
padding: 20px;
background: #12141e;
}
}
</style>
+436
View File
@@ -0,0 +1,436 @@
<template>
<div class="merchant-order-wrap">
<indexHeader></indexHeader>
<secondaryHead :text="$t('c2cMerchant.transfer_to_merchant')"></secondaryHead>
<div class="bit-center-wrap mo-box">
<!-- Progress Bar -->
<div class="progress-wrap">
<div class="step" :class="{ active: currentStep >= 1, done: currentStep > 1 }">
<div class="dot">1</div>
<div class="step-title">{{ $t('c2cMerchant.step1') }}</div>
</div>
<div class="line" :class="{ active: currentStep > 1 }"></div>
<div class="step" :class="{ active: currentStep >= 2, done: currentStep > 2 }">
<div class="dot">2</div>
<div class="step-title">{{ $t('c2cMerchant.step2') }}</div>
</div>
<div class="line" :class="{ active: currentStep > 2 }"></div>
<div class="step" :class="{ active: currentStep >= 3 }">
<div class="dot">3</div>
<div class="step-title">{{ $t('c2cMerchant.step3') }}</div>
</div>
</div>
<!-- Title + Countdown -->
<div class="hero">
<h2 class="title">{{ $t('c2cMerchant.transfer_to_merchant') }}</h2>
<div class="countdown-wrap">
<div class="countdown-label">{{ $t('c2cMerchant.pay_within') }}</div>
<div class="countdown">{{ countdownText }}</div>
</div>
</div>
<!-- Contact Buttons -->
<div class="contact-wrap">
<button
v-if="merchantContactType.toLowerCase() === 'whatsapp'"
class="contact-btn whatsapp"
@click="contactMerchant"
:disabled="!merchantContactLink"
>
<svg class="wa-icon" viewBox="0 0 24 24" width="24" height="24"><path fill="#fff" d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347z"/><path fill="#fff" d="M12 0C5.373 0 0 5.373 0 12c0 2.625.846 5.059 2.284 7.034L.789 23.492a.5.5 0 00.613.613l4.458-1.495A11.935 11.935 0 0012 24c6.627 0 12-5.373 12-12S18.627 0 12 0zm0 22c-2.34 0-4.512-.767-6.262-2.063l-.438-.337-2.65.888.888-2.65-.337-.438A9.958 9.958 0 012 12C2 6.486 6.486 2 12 2s10 4.486 10 10-4.486 10-10 10z"/></svg>
<span>WhatsApp</span>
<i class="el-icon-arrow-right"></i>
</button>
<button
v-else-if="merchantContactType.toLowerCase() === 'telegram'"
class="contact-btn telegram"
@click="contactMerchant"
:disabled="!merchantContactLink"
>
<svg class="tg-icon" viewBox="0 0 24 24" width="24" height="24"><path fill="#fff" d="M11.944 0A12 12 0 000 12a12 12 0 0012 12 12 12 0 0012-12A12 12 0 0012 0h-.056zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 01.171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.479.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>
<span>Telegram</span>
<i class="el-icon-arrow-right"></i>
</button>
<button
v-else
class="contact-btn whatsapp"
@click="contactMerchant"
:disabled="!merchantContactLink"
>
<i class="el-icon-chat-dot-round" style="font-size:22px;"></i>
<span>{{ merchantContactType || 'Contact' }}</span>
<i class="el-icon-arrow-right"></i>
</button>
</div>
<!-- Order Info Card -->
<div class="order-card">
<div class="card-title">
{{ detail.direction === 'buy' ? $t('c2cMerchant.buy') : $t('c2cMerchant.sell') }}
{{ detail.crypto_currency || '' }}
</div>
<div class="row">
<span class="label">{{ $t('c2cMerchant.you_pay') || '您要支付' }}</span>
<span class="value price">${{ formatNum(detail.total_price) }}{{ detail.fiat_currency || 'USD' }}</span>
</div>
<div class="row">
<span class="label">{{ $t('c2cMerchant.fee') }}</span>
<span class="value">${{ formatNum(detail.fee) }}{{ detail.fiat_currency || 'USD' }}</span>
</div>
<div class="row">
<span class="label">{{ $t('c2cMerchant.quantity') }}</span>
<span class="value">{{ formatNum(detail.amount) }} {{ detail.crypto_currency || '' }}</span>
</div>
<div class="row">
<span class="label">{{ $t('c2cMerchant.order_no') }}</span>
<span class="value copy-cell" @click="copyOrderNo">
<span class="order-no">{{ detail.order_no || detail.id || '-' }}</span>
<i class="el-icon-document-copy"></i>
</span>
</div>
<div class="row">
<span class="label">{{ $t('c2cMerchant.order_time') }}</span>
<span class="value">{{ detail.created_at || detail.create_time || '-' }}</span>
</div>
</div>
</div>
<indexFooter class="pc-only"></indexFooter>
</div>
</template>
<script>
import indexHeader from '@/view/indexHeader'
import indexFooter from '@/view/indexFooter'
import secondaryHead from '@/components/secondaryHead'
export default {
name: 'merchantOrder',
components: { indexHeader, indexFooter, secondaryHead },
data() {
return {
token: '',
orderId: '',
detail: {},
currentStep: 2,
remainSeconds: 30 * 60,
timer: null
}
},
computed: {
countdownText() {
const s = Math.max(0, this.remainSeconds)
const m = Math.floor(s / 60)
const sec = s % 60
const pad = n => (n < 10 ? '0' + n : '' + n)
return pad(m) + ':' + pad(sec)
},
merchantContactType() {
const m = this.detail.merchant || {}
return m.contact_type || this.detail.contact_type || ''
},
merchantContactLink() {
const m = this.detail.merchant || {}
return m.contact_link || this.detail.contact_link || ''
}
},
created() {
this.token = window.localStorage.getItem('token') || ''
if (!this.token) {
this.$router.push('/components/login')
return
}
this.orderId = this.$route.query.id || ''
if (this.orderId) {
this.fetchDetail()
}
},
beforeDestroy() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
methods: {
formatNum(v) {
if (v === undefined || v === null || v === '') return '0.00'
const n = Number(v)
if (isNaN(n)) return '0.00'
return n.toFixed(2)
},
fetchDetail() {
const i = typeof layer !== 'undefined' ? layer.load() : null
this.$http({
url: '/api/c2c_merchant/order_detail',
method: 'get',
params: { id: this.orderId },
headers: { Authorization: this.token }
})
.then(res => {
if (i !== null) layer.close(i)
if (res.data.type === 'ok') {
this.detail = res.data.message || {}
this.initCountdown()
this.resolveStep()
}
})
.catch(() => {
if (i !== null) layer.close(i)
})
},
initCountdown() {
const expireTs = this.detail.expire_time
? new Date((this.detail.expire_time + '').replace(/-/g, '/')).getTime()
: 0
if (expireTs > 0) {
const now = Date.now()
this.remainSeconds = Math.max(0, Math.floor((expireTs - now) / 1000))
} else {
this.remainSeconds = 30 * 60
}
if (this.timer) clearInterval(this.timer)
this.timer = setInterval(() => {
if (this.remainSeconds > 0) {
this.remainSeconds -= 1
} else {
clearInterval(this.timer)
this.timer = null
}
}, 1000)
},
resolveStep() {
const status = this.detail.status
if (status === 'completed' || status === 'finished' || status === 2) {
this.currentStep = 3
} else if (status === 'paid' || status === 1) {
this.currentStep = 3
} else {
this.currentStep = 2
}
},
contactMerchant() {
const link = this.merchantContactLink
if (!link) return
const type = (this.merchantContactType || '').toLowerCase()
if (type === 'whatsapp') {
const num = (link + '').replace(/[^0-9]/g, '')
if (num) window.open('https://wa.me/' + num, '_blank')
} else if (type === 'telegram') {
const handle = (link + '').replace(/^@/, '')
window.open('https://t.me/' + handle, '_blank')
} else if (link.indexOf('http') === 0) {
window.open(link, '_blank')
} else {
window.open('https://' + link, '_blank')
}
},
copyOrderNo() {
const text = this.detail.order_no || this.detail.id || ''
if (!text) return
const ta = document.createElement('textarea')
ta.value = text + ''
ta.style.position = 'fixed'
ta.style.left = '-9999px'
document.body.appendChild(ta)
ta.select()
try {
document.execCommand('copy')
if (typeof layer !== 'undefined') {
layer.msg(this.$t('legal.copy_success') || 'Copied')
}
} catch (e) {
/* noop */
}
document.body.removeChild(ta)
}
}
}
</script>
<style lang="scss" scoped>
.merchant-order-wrap {
min-height: 100vh;
background: #0e1726;
color: #c7cce6;
}
.mo-box {
padding: 30px 0 60px;
}
.pc-only { display: block; }
.h5-only { display: none; }
/* Progress */
.progress-wrap {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 30px;
.step {
display: flex;
flex-direction: column;
align-items: center;
.dot {
width: 36px;
height: 36px;
line-height: 36px;
border-radius: 50%;
background: #242840;
color: #848e9c;
text-align: center;
font-weight: 600;
margin-bottom: 8px;
}
.step-title {
color: #848e9c;
font-size: 13px;
}
&.active {
.dot { background: #00c48c; color: #fff; }
.step-title { color: #fff; }
}
&.done .dot::before {
content: '✓';
font-size: 16px;
}
&.done .dot { color: transparent; }
&.done .dot::before { color: #fff; }
}
.line {
flex: 0 0 80px;
height: 2px;
background: #242840;
margin: 0 8px 28px;
&.active { background: #00c48c; }
}
}
/* Hero */
.hero {
text-align: center;
margin-bottom: 24px;
.title {
color: #fff;
font-size: 22px;
font-weight: 600;
margin-bottom: 14px;
}
.countdown-wrap {
display: inline-block;
.countdown-label {
color: #848e9c;
font-size: 13px;
margin-bottom: 6px;
}
.countdown {
color: #ff4040;
font-size: 28px;
font-weight: 700;
letter-spacing: 2px;
}
}
}
/* Contact Buttons */
.contact-wrap {
display: flex;
gap: 16px;
margin-bottom: 30px;
.contact-btn {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 20px 16px;
border-radius: 10px;
border: none;
color: #fff;
cursor: pointer;
font-size: 15px;
font-weight: 600;
transition: all 0.15s;
.wa-icon, .tg-icon { flex-shrink: 0; }
.el-icon-arrow-right { font-size: 16px; margin-left: auto; }
&.whatsapp { background: #25d366; }
&.telegram { background: #2aabee; }
&:hover:not(:disabled) { opacity: 0.9; }
&:disabled { opacity: 0.5; cursor: not-allowed; }
}
}
/* Order Card */
.order-card {
background: #12141e;
border: 1px solid #242840;
border-radius: 10px;
padding: 20px 24px;
.card-title {
color: #fff;
font-size: 16px;
font-weight: 600;
padding-bottom: 12px;
margin-bottom: 8px;
border-bottom: 1px solid #242840;
}
.row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px dashed #1a1f30;
font-size: 14px;
&:last-child { border-bottom: none; }
.label { color: #848e9c; }
.value { color: #fff; }
.value.price { color: #00c48c; font-weight: 600; }
.value.direction.buy { color: #00c48c; font-weight: 600; }
.value.direction.sell { color: #ff4040; font-weight: 600; }
.copy-cell {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
color: #c7cce6;
i { color: #00c48c; }
&:hover .order-no { color: #00c48c; }
}
}
}
@media screen and (max-width: 800px) {
.pc-only { display: none !important; }
.h5-only { display: block !important; }
.mo-box { padding: 16px 12px 40px; }
.progress-wrap {
.line { flex: 0 0 40px; margin: 0 4px 28px; }
.step .step-title { font-size: 11px; }
}
.hero {
.title { font-size: 18px; }
.countdown { font-size: 24px; }
}
.contact-wrap {
gap: 10px;
.contact-btn {
padding: 14px 8px;
font-size: 13px;
flex-direction: column;
gap: 4px;
.sub { margin-left: 0; font-size: 11px; }
}
}
.order-card {
padding: 14px 16px;
.row { font-size: 13px; }
}
}
</style>
+292
View File
@@ -0,0 +1,292 @@
<template>
<div class="my-orders-wrap">
<indexHeader></indexHeader>
<secondaryHead :text="$t('c2cMerchant.order_records') || '訂單記錄'"></secondaryHead>
<div class="bit-center-wrap mo-box">
<!-- Status Tabs -->
<div class="status-tabs">
<div
class="tab-item"
:class="{ active: statusFilter === 'all' }"
@click="switchStatus('all')"
>{{ $t('c2cMerchant.all') || '全部' }}</div>
<div
class="tab-item"
:class="{ active: statusFilter === '0' }"
@click="switchStatus('0')"
>{{ $t('c2cMerchant.status_pending') || 'Pending' }}</div>
<div
class="tab-item"
:class="{ active: statusFilter === '1' }"
@click="switchStatus('1')"
>{{ $t('c2cMerchant.status_completed') || 'Completed' }}</div>
<div
class="tab-item"
:class="{ active: statusFilter === '2' }"
@click="switchStatus('2')"
>{{ $t('c2cMerchant.status_cancelled') || 'Cancelled' }}</div>
</div>
<!-- Order List -->
<div v-if="orders.length === 0" class="empty">{{ $t('batchData.沒有更多了') || '暂无数据' }}</div>
<div v-for="item in orders" :key="item.id" class="order-card" @click="goDetail(item.id)">
<div class="card-top">
<div class="merchant-info">
<img v-if="item.merchant && item.merchant.logo" :src="item.merchant.logo" class="m-logo" />
<div v-else class="m-avatar" :style="{ background: avatarColor(item.merchant ? item.merchant.name : '') }">
{{ ((item.merchant && item.merchant.name) || 'M').substring(0, 1).toUpperCase() }}
</div>
<span class="m-name">{{ (item.merchant && item.merchant.name) || '-' }}</span>
</div>
<span class="status-badge" :class="'s-' + item.status">{{ item.status_text }}</span>
</div>
<div class="card-row">
<span class="label">{{ $t('c2cMerchant.direction') || '方向' }}</span>
<span class="value" :class="item.direction">
{{ item.direction === 'buy' ? $t('c2cMerchant.buy') : $t('c2cMerchant.sell') }}
{{ item.crypto_currency }}
</span>
</div>
<div class="card-row">
<span class="label">{{ $t('c2cMerchant.quantity') }}</span>
<span class="value">{{ formatNum(item.amount) }} {{ item.crypto_currency }}</span>
</div>
<div class="card-row">
<span class="label">{{ $t('c2cMerchant.total') }}</span>
<span class="value price">{{ formatNum(item.total_price) }} {{ item.fiat_currency || '' }}</span>
</div>
<div class="card-row">
<span class="label">{{ $t('c2cMerchant.order_time') }}</span>
<span class="value">{{ item.created_at || '-' }}</span>
</div>
</div>
<div v-if="hasMore" class="load-more" @click="loadMore">{{ $t('c2cMerchant.load_more') || '加载更多' }}</div>
</div>
<indexFooter class="pc-only"></indexFooter>
</div>
</template>
<script>
import indexHeader from '@/view/indexHeader'
import indexFooter from '@/view/indexFooter'
import secondaryHead from '@/components/secondaryHead'
export default {
name: 'myOrders',
components: { indexHeader, indexFooter, secondaryHead },
data() {
return {
token: '',
statusFilter: 'all',
orders: [],
page: 1,
hasMore: false,
palette: ['#00c48c', '#3b68bb', '#e35744', '#a16c92', '#66756e', '#5d8cc2', '#a2b240']
}
},
created() {
this.token = window.localStorage.getItem('token') || ''
if (!this.token) {
this.$router.push('/components/login')
return
}
this.loadOrders()
},
methods: {
formatNum(v) {
if (v === undefined || v === null || v === '') return '0.00'
const n = Number(v)
if (isNaN(n)) return '0.00'
return n.toFixed(2)
},
avatarColor(name) {
if (!name) return this.palette[0]
let sum = 0
for (let i = 0; i < name.length; i++) sum += name.charCodeAt(i)
return this.palette[sum % this.palette.length]
},
switchStatus(s) {
if (this.statusFilter === s) return
this.statusFilter = s
this.page = 1
this.orders = []
this.loadOrders()
},
loadOrders() {
const i = typeof layer !== 'undefined' ? layer.load() : null
const params = { page: this.page, limit: 20 }
if (this.statusFilter !== 'all') params.status = this.statusFilter
this.$http({
url: '/api/c2c_merchant/my_orders',
method: 'get',
params,
headers: { Authorization: this.token }
})
.then(res => {
if (i !== null) layer.close(i)
if (res.data.type === 'ok') {
const data = res.data.message || {}
const list = data.data || []
if (this.page === 1) {
this.orders = list
} else {
this.orders = this.orders.concat(list)
}
this.hasMore = data.current_page < data.last_page
}
})
.catch(() => {
if (i !== null) layer.close(i)
})
},
loadMore() {
this.page++
this.loadOrders()
},
goDetail(id) {
this.$router.push({ path: '/c2cMerchantOrder', query: { id } })
}
}
}
</script>
<style lang="scss" scoped>
.my-orders-wrap {
min-height: 100vh;
background: #0e1726;
color: #c7cce6;
}
.mo-box {
padding: 20px 0 60px;
}
.pc-only { display: block; }
.status-tabs {
display: flex;
gap: 10px;
margin-bottom: 24px;
.tab-item {
padding: 8px 20px;
border-radius: 6px;
background: #12141e;
border: 1px solid #242840;
color: #848e9c;
font-size: 14px;
cursor: pointer;
transition: all 0.15s;
&.active {
background: #00c48c;
color: #fff;
border-color: #00c48c;
}
}
}
.empty {
text-align: center;
color: #848e9c;
padding: 60px 0;
font-size: 14px;
}
.order-card {
background: #12141e;
border: 1px solid #242840;
border-radius: 8px;
padding: 16px 20px;
margin-bottom: 12px;
cursor: pointer;
transition: background 0.15s;
&:hover { background: #1a1f30; }
.card-top {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 12px;
margin-bottom: 10px;
border-bottom: 1px solid #1a1f30;
}
.merchant-info {
display: flex;
align-items: center;
.m-logo {
width: 32px;
height: 32px;
border-radius: 50%;
object-fit: cover;
margin-right: 10px;
}
.m-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
color: #fff;
text-align: center;
line-height: 32px;
font-weight: 600;
margin-right: 10px;
}
.m-name {
color: #fff;
font-size: 14px;
font-weight: 600;
}
}
.status-badge {
padding: 3px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 600;
&.s-0 { background: rgba(255,165,0,0.15); color: #ffa500; }
&.s-1 { background: rgba(0,196,140,0.15); color: #00c48c; }
&.s-2 { background: rgba(132,142,156,0.15); color: #848e9c; }
&.s-3 { background: rgba(255,64,64,0.15); color: #ff4040; }
}
.card-row {
display: flex;
justify-content: space-between;
padding: 6px 0;
font-size: 13px;
.label { color: #848e9c; }
.value { color: #c7cce6; }
.value.buy { color: #00c48c; font-weight: 600; }
.value.sell { color: #ff4040; font-weight: 600; }
.value.price { color: #00c48c; font-weight: 600; }
}
}
.load-more {
text-align: center;
color: #00c48c;
font-size: 14px;
padding: 20px 0;
cursor: pointer;
&:hover { text-decoration: underline; }
}
@media screen and (max-width: 800px) {
.pc-only { display: none !important; }
.mo-box { padding: 10px 12px 40px; }
.status-tabs {
flex-wrap: wrap;
gap: 8px;
.tab-item {
padding: 6px 14px;
font-size: 13px;
}
}
.order-card {
padding: 12px 14px;
}
}
</style>