'commit'
This commit is contained in:
160
static/js/app.js
160
static/js/app.js
@@ -1,128 +1,127 @@
|
||||
const {createApp,onMounted,ref} = Vue
|
||||
|
||||
createApp({
|
||||
setup(){
|
||||
const apiBase = ref(window.location.origin || "http://localhost:8000")
|
||||
const stationHash = ref("")
|
||||
const operators = ref([
|
||||
{label:"新电途",value:"新电途"},
|
||||
{label:"特来电",value:"特来电"},
|
||||
{label:"驿来特",value:"驿来特"},
|
||||
{label:"艾特吉易充",value:"艾特吉易充"}
|
||||
])
|
||||
const selectedOperator = ref("驿来特")
|
||||
const aiText = ref("")
|
||||
const placeholder = ref("在左侧列表中选择驿来特场站,点击“查看价差”加载24小时分时电价与竞品对比,再点击“AI价差分析”获取模型给出的策略建议。")
|
||||
const placeholder = ref("点击左侧按钮查询四家供应商最新24小时分时电价,再使用右侧AI综合分析功能对各司定价策略进行对比。")
|
||||
const loading = ref(false)
|
||||
const aiLoading = ref(false)
|
||||
const stations = ref([])
|
||||
const searchKeyword = ref("")
|
||||
const selectedStation = ref(null)
|
||||
const priceTableColumns = ref([])
|
||||
const priceTableRows = ref([])
|
||||
const hourlyPricesByOperator = ref({})
|
||||
let chartInstance = null
|
||||
let es = null
|
||||
|
||||
const initChart = () => {
|
||||
const dom = document.getElementById("chart")
|
||||
if (dom && !chartInstance) {
|
||||
chartInstance = echarts.init(dom)
|
||||
}
|
||||
}
|
||||
const loadStations = async () => {
|
||||
try{
|
||||
const url = apiBase.value + "/api/ylt/stations"
|
||||
const params = searchKeyword.value ? {params:{q:searchKeyword.value}} : {}
|
||||
const res = await axios.get(url,params)
|
||||
stations.value = Array.isArray(res.data) ? res.data : []
|
||||
if (!selectedStation.value && stations.value.length > 0) {
|
||||
selectStation(stations.value[0])
|
||||
}
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
const buildPriceTable = data => {
|
||||
const cols = []
|
||||
cols.push("时段")
|
||||
cols.push("驿来特")
|
||||
data.competitors.forEach(c => {
|
||||
cols.push(c.operator)
|
||||
})
|
||||
priceTableColumns.value = cols
|
||||
|
||||
const buildPriceTable = () => {
|
||||
const rows = []
|
||||
for (let i = 0; i < data.hours.length; i++) {
|
||||
const row = []
|
||||
row.push(data.hours[i] + ":00")
|
||||
row.push(data.ylt.series[i])
|
||||
data.competitors.forEach(c => {
|
||||
row.push(c.series[i])
|
||||
for (let h = 0; h < 24; h++) {
|
||||
const row = {hour: (h.toString().padStart(2,"0") + ":00"), values: []}
|
||||
operators.value.forEach(op => {
|
||||
const series = hourlyPricesByOperator.value[op.value] || []
|
||||
const price = series[h] !== undefined ? series[h] : null
|
||||
row.values.push({operator: op.value, price})
|
||||
})
|
||||
rows.push(row)
|
||||
}
|
||||
priceTableRows.value = rows
|
||||
if (chartInstance) {
|
||||
const series = operators.value.map(op => {
|
||||
const seriesData = []
|
||||
for (let h = 0; h < 24; h++) {
|
||||
const list = hourlyPricesByOperator.value[op.value] || []
|
||||
seriesData.push(list[h] !== undefined ? list[h] : null)
|
||||
}
|
||||
const loadComparison = async () => {
|
||||
if (!stationHash.value) return
|
||||
loading.value = true
|
||||
try{
|
||||
const url = apiBase.value + "/api/ylt/pricing/comparison/" + stationHash.value
|
||||
const res = await axios.get(url)
|
||||
const data = res.data
|
||||
const hours = data.hours.map(h => h + ":00")
|
||||
const series = []
|
||||
series.push({
|
||||
name:"驿来特",
|
||||
type:"line",
|
||||
smooth:true,
|
||||
data:data.ylt.series
|
||||
return {name: op.label, type:"line", smooth:true, data:seriesData}
|
||||
})
|
||||
data.competitors.forEach(c=>{
|
||||
series.push({
|
||||
name:c.operator,
|
||||
type:"line",
|
||||
smooth:true,
|
||||
data:c.series
|
||||
})
|
||||
})
|
||||
buildPriceTable(data)
|
||||
const hours = rows.map(r => r.hour)
|
||||
const option = {
|
||||
tooltip:{trigger:"axis"},
|
||||
legend:{data:series.map(s=>s.name),textStyle:{color:"#e5e7eb"}},
|
||||
legend:{data:operators.value.map(o=>o.label),textStyle:{color:"#e5e7eb"}},
|
||||
xAxis:{type:"category",data:hours,axisLine:{lineStyle:{color:"#4b5563"}},axisLabel:{color:"#9ca3af"}},
|
||||
yAxis:{type:"value",name:"元/度",axisLine:{lineStyle:{color:"#4b5563"}},axisLabel:{color:"#9ca3af"},splitLine:{lineStyle:{color:"#111827"}}},
|
||||
grid:{left:40,right:20,top:30,bottom:30},
|
||||
series:series
|
||||
}
|
||||
initChart()
|
||||
chartInstance.setOption(option)
|
||||
}
|
||||
}
|
||||
|
||||
const loadAllOperatorsPrices = async () => {
|
||||
loading.value = true
|
||||
try{
|
||||
const res = await axios.get(apiBase.value + "/api/operators/hourly-prices")
|
||||
if (res && res.data && Array.isArray(res.data.operators)) {
|
||||
const dict = {}
|
||||
res.data.operators.forEach(item => {
|
||||
dict[item.operator] = item.series || []
|
||||
})
|
||||
hourlyPricesByOperator.value = dict
|
||||
buildPriceTable()
|
||||
}
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}finally{
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const selectStation = s => {
|
||||
selectedStation.value = s
|
||||
stationHash.value = s.station_hash
|
||||
loadComparison()
|
||||
|
||||
const loadOperatorPrices = async () => {
|
||||
if (!selectedOperator.value) return
|
||||
await loadAllOperatorsPrices()
|
||||
}
|
||||
const startAiStream = () => {
|
||||
if (!selectedStation.value || !stationHash.value) return
|
||||
if (es) {
|
||||
es.close()
|
||||
es = null
|
||||
|
||||
const exportAllPrices = async () => {
|
||||
try{
|
||||
loading.value = true
|
||||
const res = await axios.get(apiBase.value + "/api/export/prices-zip",{responseType:"blob"})
|
||||
const blob = new Blob([res.data],{type:"application/zip"})
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement("a")
|
||||
a.href = url
|
||||
a.download = "多供应商电价导出.zip"
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}finally{
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const startAiAnalysis = async () => {
|
||||
if (aiLoading.value) return
|
||||
aiText.value = ""
|
||||
aiLoading.value = true
|
||||
const url = apiBase.value + "/api/ylt/pricing/comparison/" + stationHash.value + "/sse"
|
||||
es = new EventSource(url)
|
||||
es.onmessage = ev => {
|
||||
aiText.value += ev.data
|
||||
}
|
||||
es.onerror = () => {
|
||||
try{
|
||||
const res = await axios.get(apiBase.value + "/api/ai/pricing/strategy-summary")
|
||||
aiText.value = res.data && res.data.summary ? res.data.summary : ""
|
||||
}catch(e){
|
||||
console.error(e)
|
||||
}finally{
|
||||
aiLoading.value = false
|
||||
if (es) {
|
||||
es.close()
|
||||
es = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
initChart()
|
||||
loadStations()
|
||||
loadAllOperatorsPrices()
|
||||
})
|
||||
|
||||
const formatCell = v => {
|
||||
if (v === null || v === undefined || v === "") return "-"
|
||||
if (typeof v === "number") {
|
||||
@@ -131,6 +130,7 @@ return v.toFixed(2)
|
||||
}
|
||||
return v
|
||||
}
|
||||
return {stationHash,aiText,placeholder,loading,aiLoading,stations,searchKeyword,selectedStation,priceTableColumns,priceTableRows,loadStations,loadComparison,selectStation,startAiStream,formatCell}
|
||||
|
||||
return {apiBase,operators,selectedOperator,aiText,placeholder,loading,aiLoading,priceTableRows,loadOperatorPrices,loadAllOperatorsPrices,exportAllPrices,startAiAnalysis,formatCell}
|
||||
}
|
||||
}).mount("#app")
|
||||
|
||||
Reference in New Issue
Block a user