Initial commit: Add logistics and order_detail message types
Some checks failed
Lock Threads / action (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
Some checks failed
Lock Threads / action (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
- Add Logistics component with progress tracking - Add OrderDetail component for order information - Support data-driven steps and actions - Add blue color scale to widget SCSS - Fix node overflow and progress bar rendering issues - Add English translations for dashboard components Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
373
app/javascript/shared/components/ChatTable.vue
Normal file
373
app/javascript/shared/components/ChatTable.vue
Normal file
@@ -0,0 +1,373 @@
|
||||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
headers: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
rows: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
// Check if any row has a button
|
||||
hasButtonColumn() {
|
||||
return this.rows.some(row => {
|
||||
const lastItem = row[row.length - 1];
|
||||
return lastItem && typeof lastItem === 'object' && lastItem.button;
|
||||
});
|
||||
},
|
||||
// Get the first row with button to determine column count
|
||||
firstRowWithButton() {
|
||||
return this.rows.find(row => {
|
||||
const lastItem = row[row.length - 1];
|
||||
return lastItem && typeof lastItem === 'object' && lastItem.button;
|
||||
});
|
||||
},
|
||||
// Get number of data columns (excluding button)
|
||||
dataColumnCount() {
|
||||
if (this.firstRowWithButton) {
|
||||
return this.firstRowWithButton.length - 1;
|
||||
}
|
||||
// If no button row, check first row
|
||||
if (this.rows.length > 0) {
|
||||
return this.rows[0].length;
|
||||
}
|
||||
return this.headers.length;
|
||||
},
|
||||
// Check if headers has button header column
|
||||
hasButtonHeader() {
|
||||
return this.headers.length > this.dataColumnCount;
|
||||
},
|
||||
// Get data headers (all headers if no button header, otherwise all except last)
|
||||
displayHeaders() {
|
||||
if (this.hasButtonHeader) {
|
||||
return this.headers.slice(0, -1);
|
||||
}
|
||||
return this.headers;
|
||||
},
|
||||
// Get button header text
|
||||
buttonHeaderText() {
|
||||
if (this.hasButtonHeader) {
|
||||
return this.headers[this.headers.length - 1];
|
||||
}
|
||||
return '';
|
||||
},
|
||||
// Check if button header should be shown
|
||||
showButtonHeader() {
|
||||
return this.hasButtonColumn && this.hasButtonHeader;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions('conversation', ['sendMessage']),
|
||||
onButtonClick(button) {
|
||||
if (!button) return;
|
||||
|
||||
const { reply } = button;
|
||||
if (reply) {
|
||||
this.sendMessage({ content: reply });
|
||||
}
|
||||
},
|
||||
// Get display cells for a row
|
||||
getDisplayCells(row) {
|
||||
const lastItem = row[row.length - 1];
|
||||
const hasButton = lastItem && typeof lastItem === 'object' && lastItem.button;
|
||||
|
||||
if (hasButton) {
|
||||
return row.slice(0, -1);
|
||||
}
|
||||
return row;
|
||||
},
|
||||
// Get button from row
|
||||
getRowButton(row) {
|
||||
const lastItem = row[row.length - 1];
|
||||
if (lastItem && typeof lastItem === 'object' && lastItem.button) {
|
||||
return lastItem.button;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="table-message-container">
|
||||
<div class="table-bubble">
|
||||
<h4 v-if="title" class="table-title">
|
||||
{{ title }}
|
||||
</h4>
|
||||
<div class="table-scroll">
|
||||
<table class="table-content">
|
||||
<thead>
|
||||
<tr class="header-row">
|
||||
<th
|
||||
v-for="(header, index) in displayHeaders"
|
||||
:key="'header-' + index"
|
||||
class="header-cell"
|
||||
>
|
||||
{{ header }}
|
||||
</th>
|
||||
<th v-if="showButtonHeader" class="header-cell sticky-header">
|
||||
{{ buttonHeaderText }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(row, rowIndex) in rows"
|
||||
:key="'row-' + rowIndex"
|
||||
class="data-row"
|
||||
>
|
||||
<td
|
||||
v-for="(cell, cellIndex) in getDisplayCells(row)"
|
||||
:key="'cell-' + cellIndex"
|
||||
class="data-cell"
|
||||
>
|
||||
{{ cell }}
|
||||
</td>
|
||||
<td v-if="hasButtonColumn" class="data-cell sticky-cell">
|
||||
<button
|
||||
v-if="getRowButton(row)"
|
||||
class="table-action-button"
|
||||
@click="onButtonClick(getRowButton(row))"
|
||||
>
|
||||
{{ getRowButton(row).text }}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.table-message-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.table-bubble {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
border: 1px solid #e2e8f0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dark .table-bubble {
|
||||
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
||||
border-color: #475569;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25), 0 1px 3px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.table-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
color: #1e293b;
|
||||
margin: 0 0 12px 0;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.dark .table-title {
|
||||
color: #f1f5f9;
|
||||
border-bottom-color: #475569;
|
||||
}
|
||||
|
||||
.table-scroll {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
border-radius: 8px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.table-content {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.header-row {
|
||||
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
||||
}
|
||||
|
||||
.header-cell {
|
||||
padding: 10px 12px;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.header-cell:first-child {
|
||||
border-top-left-radius: 6px;
|
||||
}
|
||||
|
||||
.sticky-header {
|
||||
position: sticky;
|
||||
right: 0;
|
||||
background: inherit;
|
||||
z-index: 10;
|
||||
min-width: 100px;
|
||||
text-align: center;
|
||||
box-shadow: -2px 0 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.data-row {
|
||||
background-color: #ffffff;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.data-row:hover {
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.dark .data-row {
|
||||
background-color: #1e293b;
|
||||
}
|
||||
|
||||
.dark .data-row:hover {
|
||||
background-color: #334155;
|
||||
}
|
||||
|
||||
.data-row:nth-child(even) {
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
|
||||
.dark .data-row:nth-child(even) {
|
||||
background-color: #0f172a;
|
||||
}
|
||||
|
||||
.data-cell {
|
||||
padding: 10px 12px;
|
||||
color: #475569;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
border-right: 1px solid #f1f5f9;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dark .data-cell {
|
||||
color: #cbd5e1;
|
||||
border-bottom-color: #334155;
|
||||
border-right-color: #1e293b;
|
||||
}
|
||||
|
||||
.sticky-cell {
|
||||
position: sticky;
|
||||
right: 0;
|
||||
background: inherit;
|
||||
z-index: 10;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
box-shadow: -2px 0 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.dark .sticky-cell {
|
||||
background-color: #1e293b;
|
||||
box-shadow: -2px 0 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.data-row:hover .sticky-cell {
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.dark .data-row:hover .sticky-cell {
|
||||
background-color: #334155;
|
||||
}
|
||||
|
||||
.data-row:nth-child(even) .sticky-cell {
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
|
||||
.dark .data-row:nth-child(even) .sticky-cell {
|
||||
background-color: #0f172a;
|
||||
}
|
||||
|
||||
.data-row:last-child .data-cell {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.data-row:last-child .data-cell:first-child {
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
|
||||
.data-row:last-child .sticky-cell {
|
||||
border-bottom-right-radius: 6px;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.table-action-button {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #3b82f6;
|
||||
background: transparent;
|
||||
border: 1px solid #3b82f6;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-action-button:hover {
|
||||
background: #3b82f6;
|
||||
color: #ffffff;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.table-action-button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.dark .table-action-button {
|
||||
color: #60a5fa;
|
||||
border-color: #60a5fa;
|
||||
}
|
||||
|
||||
.dark .table-action-button:hover {
|
||||
background: #60a5fa;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 480px) {
|
||||
.table-bubble {
|
||||
padding: 12px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header-cell,
|
||||
.data-cell {
|
||||
padding: 8px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table-action-button {
|
||||
padding: 5px 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user